1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-10-25 09:27:01 +00:00

Compare commits

...

19 Commits

Author SHA1 Message Date
Thomas Harte
ab02f82470 Merge pull request #543 from TomHarte/CFBundleTypeOSTypes
Removes `LSItemContentTypes` so as not to reject files.
2018-09-09 17:49:16 -04:00
Thomas Harte
1e3318816c Removes LSItemContentTypes so as not to reject files. 2018-09-09 17:47:03 -04:00
Thomas Harte
3a3dec92c7 Merge pull request #540 from MaddTheSane/plistFix
Remove LSItemContentTypes
2018-09-09 10:07:19 -04:00
Thomas Harte
5a5fc1ae1a Merge pull request #541 from TomHarte/Annunciator3
Implements the two undocumented annunciator 3 graphics modes
2018-09-09 10:06:52 -04:00
Thomas Harte
8d79a1e381 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".
2018-09-09 10:06:21 -04:00
Thomas Harte
d70f5da94e Attempts an implementation of the undocumented low res + annunciator 3 graphics mode. 2018-09-08 20:51:15 -04:00
C.W. Betts
05d4274019 Remove LSItemContentTypes: they should be unique identifiers, not generic types like public.item or public.data.
This can result in strange icons showing up in the wrong places.

Also added a category type.
2018-09-07 16:39:52 -06:00
Thomas Harte
afeec09902 Gets explicit about DHIRES being annunciator 3; implements four-colour high res mode. 2018-09-06 23:23:19 -04:00
Thomas Harte
0526ac2ee2 Slightly increases const correctness.
The converters from source data to output pixels do not modify the source data. It's a shame there's no `restrict` in C++.
2018-09-05 11:36:40 -04:00
Thomas Harte
6725ee2190 Merge pull request #539 from TomHarte/40ColumnTextCorruption
Corrects 40-column alternative text mode corruption
2018-09-05 10:27:09 -04:00
Thomas Harte
8b661fb90f Introduces an extra level of indirection for text mapping. 2018-09-05 10:26:08 -04:00
Thomas Harte
dab7d3db1b Merge branch 'master' into 40ColumnTextCorruption 2018-08-30 20:24:47 -04:00
Thomas Harte
1cba3d48d9 Merge pull request #538 from TomHarte/AppleDecodingAgain
Correction: 0xc011 et al get the keyboard value in bits 0 to 6...
2018-08-30 20:19:48 -04:00
Thomas Harte
d53b38ec7e Correction: 0xc011 et al get the keyboard value in bits 0 to 6 and the switch value in bit 7. 2018-08-30 20:18:36 -04:00
Thomas Harte
5d0f47eda2 Merge pull request #536 from TomHarte/AppleDecoding
Adds mirrors for keyboard input and the audio toggle.
2018-08-27 21:14:48 -04:00
Thomas Harte
2e04c4442c Adds mirrors for keyboard input and the audio toggle. 2018-08-27 21:14:21 -04:00
Thomas Harte
f639cdc8ad Merge pull request #535 from TomHarte/DSKFixes
Corrects Apple DSK track length, inter-track skew, and Pro-DOS volume number.
2018-08-27 21:07:11 -04:00
Thomas Harte
71ec7624ca Corrects Apple DSK track length, inter-track skew, and Pro-DOS volume number. 2018-08-27 20:56:25 -04:00
Thomas Harte
0599d9602e Ensures no out-of-bounds accesses to inverses on a IIe. 2018-08-26 23:02:31 -04:00
7 changed files with 341 additions and 184 deletions

View File

@@ -88,6 +88,14 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
uint8_t keyboard_input_ = 0x00;
bool key_is_down_ = false;
uint8_t get_keyboard_input() {
if(string_serialiser_) {
return string_serialiser_->head() | 0x80;
} else {
return keyboard_input_;
}
}
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Audio::Toggle audio_toggle_;
Outputs::Speaker::LowpassSpeaker<Audio::Toggle> speaker_;
@@ -466,11 +474,11 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
default: break;
case 0xc000:
if(string_serialiser_) {
*value = string_serialiser_->head() | 0x80;
} else {
*value = keyboard_input_;
}
*value = get_keyboard_input();
break;
case 0xc001: case 0xc002: case 0xc003: case 0xc004: case 0xc005: case 0xc006: case 0xc007:
case 0xc008: case 0xc009: case 0xc00a: case 0xc00b: case 0xc00c: case 0xc00d: case 0xc00e: case 0xc00f:
*value = (*value & 0x80) | (get_keyboard_input() & 0x7f);
break;
case 0xc061: // Switch input 0.
@@ -507,22 +515,27 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
} break;
// The IIe-only state reads follow...
case 0xc011: if(is_iie()) *value = (*value & 0x7f) | (language_card_.bank1 ? 0x80 : 0x00); break;
case 0xc012: if(is_iie()) *value = (*value & 0x7f) | (language_card_.read ? 0x80 : 0x00); break;
case 0xc013: if(is_iie()) *value = (*value & 0x7f) | (read_auxiliary_memory_ ? 0x80 : 0x00); break;
case 0xc014: if(is_iie()) *value = (*value & 0x7f) | (write_auxiliary_memory_ ? 0x80 : 0x00); break;
case 0xc015: if(is_iie()) *value = (*value & 0x7f) | (internal_CX_rom_ ? 0x80 : 0x00); break;
case 0xc016: if(is_iie()) *value = (*value & 0x7f) | (alternative_zero_page_ ? 0x80 : 0x00); break;
case 0xc017: if(is_iie()) *value = (*value & 0x7f) | (slot_C3_rom_ ? 0x80 : 0x00); break;
case 0xc018: if(is_iie()) *value = (*value & 0x7f) | (video_->get_80_store() ? 0x80 : 0x00); break;
case 0xc019: if(is_iie()) *value = (*value & 0x7f) | (video_->get_is_vertical_blank(cycles_since_video_update_) ? 0x00 : 0x80); break;
case 0xc01a: if(is_iie()) *value = (*value & 0x7f) | (video_->get_text() ? 0x80 : 0x00); break;
case 0xc01b: if(is_iie()) *value = (*value & 0x7f) | (video_->get_mixed() ? 0x80 : 0x00); break;
case 0xc01c: if(is_iie()) *value = (*value & 0x7f) | (video_->get_page2() ? 0x80 : 0x00); break;
case 0xc01d: if(is_iie()) *value = (*value & 0x7f) | (video_->get_high_resolution() ? 0x80 : 0x00); break;
case 0xc01e: if(is_iie()) *value = (*value & 0x7f) | (video_->get_alternative_character_set() ? 0x80 : 0x00); break;
case 0xc01f: if(is_iie()) *value = (*value & 0x7f) | (video_->get_80_columns() ? 0x80 : 0x00); break;
case 0xc07f: if(is_iie()) *value = (*value & 0x7f) | (video_->get_double_high_resolution() ? 0x80 : 0x00); break;
#define IIeSwitchRead(s) *value = get_keyboard_input(); if(is_iie()) *value = (*value & 0x7f) | (s ? 0x80 : 0x00);
case 0xc011: IIeSwitchRead(language_card_.bank1); break;
case 0xc012: IIeSwitchRead(language_card_.read); break;
case 0xc013: IIeSwitchRead(read_auxiliary_memory_); break;
case 0xc014: IIeSwitchRead(write_auxiliary_memory_); break;
case 0xc015: IIeSwitchRead(internal_CX_rom_); break;
case 0xc016: IIeSwitchRead(alternative_zero_page_); break;
case 0xc017: IIeSwitchRead(slot_C3_rom_); break;
case 0xc018: IIeSwitchRead(video_->get_80_store()); break;
case 0xc019: IIeSwitchRead(video_->get_is_vertical_blank(cycles_since_video_update_)); break;
case 0xc01a: IIeSwitchRead(video_->get_text()); break;
case 0xc01b: IIeSwitchRead(video_->get_mixed()); break;
case 0xc01c: IIeSwitchRead(video_->get_page2()); break;
case 0xc01d: IIeSwitchRead(video_->get_high_resolution()); break;
case 0xc01e: IIeSwitchRead(video_->get_alternative_character_set()); break;
case 0xc01f: IIeSwitchRead(video_->get_80_columns()); break;
#undef IIeSwitchRead
case 0xc07f:
if(is_iie()) *value = (*value & 0x7f) | (video_->get_annunciator_3() ? 0x80 : 0x00);
break;
}
} else {
// Write-only switches. All IIe as currently implemented.
@@ -598,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();
@@ -623,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;
@@ -640,7 +653,8 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
}
break;
case 0xc030:
case 0xc030: case 0xc031: case 0xc032: case 0xc033: case 0xc034: case 0xc035: case 0xc036: case 0xc037:
case 0xc038: case 0xc039: case 0xc03a: case 0xc03b: case 0xc03c: case 0xc03d: case 0xc03e: case 0xc03f:
update_audio();
audio_toggle_.set_output(!audio_toggle_.get_output());
break;

View File

@@ -27,6 +27,23 @@ VideoBase::VideoBase(bool is_iie, std::function<void(Cycles)> &&target) :
crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite);
crt_->set_visible_area(Outputs::CRT::Rect(0.118f, 0.122f, 0.77f, 0.77f));
crt_->set_immediate_default_phase(0.0f);
character_zones[0].xor_mask = 0;
character_zones[0].address_mask = 0x3f;
character_zones[1].xor_mask = 0;
character_zones[1].address_mask = 0x3f;
character_zones[2].xor_mask = 0;
character_zones[2].address_mask = 0x3f;
character_zones[3].xor_mask = 0;
character_zones[3].address_mask = 0x3f;
if(is_iie) {
character_zones[0].xor_mask =
character_zones[2].xor_mask =
character_zones[3].xor_mask = 0xff;
character_zones[2].address_mask =
character_zones[3].address_mask = 0xff;
}
}
Outputs::CRT::CRT *VideoBase::get_crt() {
@@ -40,6 +57,13 @@ void VideoBase::set_alternative_character_set(bool alternative_character_set) {
set_alternative_character_set_ = alternative_character_set;
deferrer_.defer(Cycles(2), [=] {
alternative_character_set_ = alternative_character_set;
if(alternative_character_set) {
character_zones[1].address_mask = 0xff;
character_zones[1].xor_mask = 0;
} else {
character_zones[1].address_mask = 0x3f;
character_zones[1].xor_mask = flash_mask();
}
});
}
@@ -107,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) {
@@ -136,19 +161,10 @@ void VideoBase::set_character_rom(const std::vector<uint8_t> &character_rom) {
}
}
void VideoBase::output_text(uint8_t *target, uint8_t *source, size_t length, size_t pixel_row) const {
const uint8_t inverses[] = {
0xff,
is_iie_ ? static_cast<uint8_t>(0xff) : static_cast<uint8_t>((flash_ / flash_length) * 0xff),
is_iie_ ? static_cast<uint8_t>(0xff) : static_cast<uint8_t>(0x00),
is_iie_ ? static_cast<uint8_t>(0xff) : static_cast<uint8_t>(0x00)
};
const int or_mask = alternative_character_set_ ? 0x100 : 0x000;
const int and_mask = is_iie_ ? ~0 : 0x3f;
void VideoBase::output_text(uint8_t *target, const uint8_t *const source, size_t length, size_t pixel_row) const {
for(size_t c = 0; c < length; ++c) {
const int character = (source[c] | or_mask) & and_mask;
const uint8_t xor_mask = inverses[character >> 6];
const int character = source[c] & character_zones[source[c] >> 6].address_mask;
const uint8_t xor_mask = character_zones[source[c] >> 6].xor_mask;
const std::size_t character_address = static_cast<std::size_t>(character << 3) + pixel_row;
const uint8_t character_pattern = character_rom_[character_address] ^ xor_mask;
@@ -165,21 +181,24 @@ void VideoBase::output_text(uint8_t *target, uint8_t *source, size_t length, siz
}
}
void VideoBase::output_double_text(uint8_t *target, uint8_t *source, uint8_t *auxiliary_source, size_t length, size_t pixel_row) const {
void VideoBase::output_double_text(uint8_t *target, const uint8_t *const source, const uint8_t *const auxiliary_source, size_t length, size_t pixel_row) const {
for(size_t c = 0; c < length; ++c) {
const std::size_t character_addresses[2] = {
static_cast<std::size_t>(
auxiliary_source[c] << 3
(auxiliary_source[c] & character_zones[auxiliary_source[c] >> 6].address_mask) << 3
) + pixel_row,
static_cast<std::size_t>(
source[c] << 3
) + pixel_row,
(source[c] & character_zones[source[c] >> 6].address_mask) << 3
) + pixel_row
};
const size_t pattern_offset = alternative_character_set_ ? (256*8) : 0;
const uint8_t character_patterns[2] = {
character_rom_[character_addresses[0] + pattern_offset],
character_rom_[character_addresses[1] + pattern_offset],
static_cast<uint8_t>(
character_rom_[character_addresses[0]] ^ character_zones[auxiliary_source[c] >> 6].xor_mask
),
static_cast<uint8_t>(
character_rom_[character_addresses[1]] ^ character_zones[source[c] >> 6].xor_mask
)
};
// The character ROM is output MSB to LSB rather than LSB to MSB.
@@ -202,7 +221,7 @@ void VideoBase::output_double_text(uint8_t *target, uint8_t *source, uint8_t *au
}
}
void VideoBase::output_low_resolution(uint8_t *target, uint8_t *source, size_t length, int column, int row) const {
void VideoBase::output_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
@@ -224,7 +243,21 @@ void VideoBase::output_low_resolution(uint8_t *target, uint8_t *source, size_t l
}
}
void VideoBase::output_double_low_resolution(uint8_t *target, uint8_t *source, uint8_t *auxiliary_source, size_t length, int column, int row) const {
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) {
if((column + static_cast<int>(c))&1) {
@@ -254,11 +287,13 @@ void VideoBase::output_double_low_resolution(uint8_t *target, uint8_t *source, u
}
}
void VideoBase::output_high_resolution(uint8_t *target, uint8_t *source, size_t length) const {
void VideoBase::output_high_resolution(uint8_t *target, const uint8_t *const source, size_t length) const {
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;
@@ -281,7 +316,7 @@ void VideoBase::output_high_resolution(uint8_t *target, uint8_t *source, size_t
}
}
void VideoBase::output_double_high_resolution(uint8_t *target, uint8_t *source, uint8_t *auxiliary_source, size_t length) const {
void VideoBase::output_double_high_resolution(uint8_t *target, const uint8_t *const source, const uint8_t *const auxiliary_source, size_t length) const {
for(size_t c = 0; c < length; ++c) {
target[0] = auxiliary_source[c] & 0x01;
target[1] = auxiliary_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> &);
@@ -152,17 +153,21 @@ class VideoBase {
// State affecting logical state.
int row_ = 0, column_ = 0, flash_ = 0;
uint8_t flash_mask() {
return static_cast<uint8_t>((flash_ / flash_length) * 0xff);
}
// 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.
@@ -173,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
@@ -195,35 +201,51 @@ class VideoBase {
bool is_iie_ = false;
static const int flash_length = 8406;
// Describes the current text mode mapping from in-memory character index
// to output character.
struct CharacterMapping {
uint8_t address_mask;
uint8_t xor_mask;
};
CharacterMapping character_zones[4];
/*!
Outputs 40-column text to @c target, using @c length bytes from @c source.
*/
void output_text(uint8_t *target, uint8_t *source, size_t length, size_t pixel_row) const;
void output_text(uint8_t *target, const uint8_t *source, size_t length, size_t pixel_row) const;
/*!
Outputs 80-column text to @c target, drawing @c length columns from @c source and @c auxiliary_source.
*/
void output_double_text(uint8_t *target, uint8_t *source, uint8_t *auxiliary_source, size_t length, size_t pixel_row) const;
void output_double_text(uint8_t *target, const uint8_t *source, const uint8_t *auxiliary_source, size_t length, size_t pixel_row) const;
/*!
Outputs 40-column low-resolution graphics to @c target, drawing @c length columns from @c source.
*/
void output_low_resolution(uint8_t *target, uint8_t *source, size_t length, int column, int row) const;
void output_low_resolution(uint8_t *target, const uint8_t *source, size_t length, int column, int row) const;
/*!
Outputs 80-column low-resolution graphics to @c target, drawing @c length columns from @c source and @c auxiliary_source.
*/
void output_double_low_resolution(uint8_t *target, uint8_t *source, uint8_t *auxiliary_source, size_t length, int column, int row) const;
void output_double_low_resolution(uint8_t *target, const uint8_t *source, const uint8_t *auxiliary_source, size_t length, int column, int row) const;
/*!
Outputs 40-column high-resolution graphics to @c target, drawing @c length columns from @c source.
*/
void output_high_resolution(uint8_t *target, uint8_t *source, size_t length) const;
void output_high_resolution(uint8_t *target, const uint8_t *source, size_t length) const;
/*!
Outputs 80-column double-high-resolution graphics to @c target, drawing @c length columns from @c source.
*/
void output_double_high_resolution(uint8_t *target, uint8_t *source, 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.
ClockDeferrer<Cycles> deferrer_;
@@ -355,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_);
@@ -428,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],
@@ -516,6 +548,9 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
if(!column_) {
row_ = (row_ + 1) % 262;
flash_ = (flash_ + 1) % (2 * flash_length);
if(!alternative_character_set_) {
character_zones[1].xor_mask = flash_mask();
}
// Add an extra half a colour cycle of blank; this isn't counted in the run_for
// count explicitly but is promised.
@@ -525,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;
}
}

View File

@@ -14,38 +14,42 @@
</array>
<key>CFBundleTypeIconFile</key>
<string>cartridge.png</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeName</key>
<string>Atari 2600 Cartridge</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>rom</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>chip.png</string>
<key>CFBundleTypeName</key>
<string>ROM Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
@@ -53,100 +57,110 @@
<string>uef</string>
<string>uef.gz</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette.png</string>
<key>CFBundleTypeName</key>
<string>Electron/BBC UEF Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>prg</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>floppy525.png</string>
<key>CFBundleTypeName</key>
<string>Commodore Program</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>tap</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette.png</string>
<key>CFBundleTypeName</key>
<string>Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>g64</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>floppy525.png</string>
<key>CFBundleTypeName</key>
<string>Commodore Disk</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>d64</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>floppy525.png</string>
<key>CFBundleTypeName</key>
<string>Commodore 1540/1 Disk</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
@@ -157,40 +171,44 @@
<string>adl</string>
<string>adm</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>floppy35.png</string>
<key>CFBundleTypeName</key>
<string>Electron/BBC Disk Image</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>dsk</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>floppy35.png</string>
<key>CFBundleTypeName</key>
<string>Disk Image</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
@@ -198,20 +216,22 @@
<string>o</string>
<string>80</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette.png</string>
<key>CFBundleTypeName</key>
<string>ZX80 Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
@@ -220,178 +240,196 @@
<string>81</string>
<string>p81</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette.png</string>
<key>CFBundleTypeName</key>
<string>ZX81 Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>csw</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette.png</string>
<key>CFBundleTypeName</key>
<string>Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>tzx</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette.png</string>
<key>CFBundleTypeName</key>
<string>Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>cdt</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette.png</string>
<key>CFBundleTypeName</key>
<string>Amstrad CPC Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>hfe</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>floppy35.png</string>
<key>CFBundleTypeName</key>
<string>HxC Disk Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>cas</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette.png</string>
<key>CFBundleTypeName</key>
<string>MSX Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>dmk</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>floppy35.png</string>
<key>CFBundleTypeName</key>
<string>Disk Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>tsx</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette.png</string>
<key>CFBundleTypeName</key>
<string>MSX Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>col</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cartridge.png</string>
<key>CFBundleTypeName</key>
<string>ColecoVision Cartridge</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
@@ -401,20 +439,22 @@
<string>do</string>
<string>po</string>
</array>
<key>CFBundleTypeOSTypes</key>
<array>
<string>????</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>floppy525.png</string>
<key>CFBundleTypeName</key>
<string>Apple II Disk Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<false/>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
</array>
<key>CFBundleExecutable</key>
@@ -436,7 +476,7 @@
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationCategoryType</key>
<string></string>
<string>public.app-category.entertainment</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>

View File

@@ -75,19 +75,24 @@ std::shared_ptr<Track> AppleDSK::get_track_at_position(Track::Address address) {
// In either case below, the code aims for exactly 50,000 bits per track.
if(sectors_per_track_ == 16) {
// Write gap 1.
segment += Encodings::AppleGCR::six_and_two_sync(16);
segment += Encodings::AppleGCR::six_and_two_sync(24);
// Write the sectors.
for(uint8_t c = 0; c < 16; ++c) {
segment += Encodings::AppleGCR::header(254, track, c);
segment += Encodings::AppleGCR::header(is_prodos_ ? 0x01 : 0xfe, track, c); // Volume number is 0xfe for DOS 3.3, 0x01 for Pro-DOS.
segment += Encodings::AppleGCR::six_and_two_sync(7); // Gap 2: 7 sync words.
segment += Encodings::AppleGCR::six_and_two_data(&track_data[logical_sector_for_physical_sector(c) * 256]);
segment += Encodings::AppleGCR::six_and_two_sync(16); // Gap 3: 16 sync words.
segment += Encodings::AppleGCR::six_and_two_sync(20); // Gap 3: 20 sync words.
}
} else {
// TODO: 5 and 3, 13-sector format. If DSK actually supports it?
}
// Apply inter-track skew; skew is about 40ms between each track; assuming 300RPM that's
// 1/5th of a revolution.
const size_t offset_in_fifths = address.position.as_int() % 5;
segment.rotate_right(offset_in_fifths * segment.data.size() / 5);
return std::make_shared<PCMTrack>(segment);
}

View File

@@ -50,6 +50,25 @@ PCMSegment &PCMSegment::operator +=(const PCMSegment &rhs) {
return *this;
}
void PCMSegment::rotate_right(size_t length) {
length %= data.size();
if(!length) return;
// To rotate to the right, front-insert the proper number
// of bits from the end and then resize. To rotate to
// the left, do the opposite.
std::vector<uint8_t> data_copy;
if(length > 0) {
data_copy.insert(data_copy.end(), data.end() - static_cast<off_t>(length), data.end());
data.erase(data.end() - static_cast<off_t>(length), data.end());
data.insert(data.begin(), data_copy.begin(), data_copy.end());
} else {
data_copy.insert(data_copy.end(), data.begin(), data.begin() - static_cast<off_t>(length));
data.erase(data.begin(), data.begin() - static_cast<off_t>(length));
data.insert(data.end(), data_copy.begin(), data_copy.end());
}
}
Storage::Disk::Track::Event PCMSegmentEventSource::get_next_event() {
// track the initial bit pointer for potentially considering whether this was an
// initial index hole or a subsequent one later on

View File

@@ -105,6 +105,13 @@ struct PCMSegment {
data.clear();
}
/*!
Rotates all bits in this segment by @c length bits.
@c length is signed; to rotate left provide a negative number.
*/
void rotate_right(size_t length);
/*!
Produces a byte buffer where the contents of @c data are serialised into bytes