mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-27 00:30:26 +00:00
Merge branch 'master' into Plus4VideoOptions
This commit is contained in:
commit
457b28c22c
@ -251,13 +251,11 @@ public:
|
||||
}
|
||||
|
||||
const auto output = io_output_ | ~io_direction_;
|
||||
// tape_player_->set_motor_control(~output & 0x08);
|
||||
tape_player_->set_motor_control(~output & 0x08);
|
||||
serial_port_.set_output(Serial::Line::Data, Serial::LineLevel(~output & 0x01));
|
||||
serial_port_.set_output(Serial::Line::Clock, Serial::LineLevel(~output & 0x02));
|
||||
serial_port_.set_output(Serial::Line::Attention, Serial::LineLevel(~output & 0x04));
|
||||
}
|
||||
|
||||
// printf("%04x: %02x %c\n", address, *value, isReadOperation(operation) ? 'r' : 'w');
|
||||
} else if(address < 0xfd00 || address >= 0xff40) {
|
||||
if(isReadOperation(operation)) {
|
||||
*value = map_.read(address);
|
||||
@ -269,8 +267,27 @@ public:
|
||||
if(isReadOperation(operation)) {
|
||||
// printf("TODO: read @ %04x\n", address);
|
||||
if((address & 0xfff0) == 0xfd10) {
|
||||
tape_player_->set_motor_control(true);
|
||||
*value = 0xff ^ 0x4; // Seems to detect the play button.
|
||||
// 6529 parallel port, about which I know only what I've found in kernel ROM disassemblies.
|
||||
|
||||
// If play button is not currently pressed and this read is immediately followed by
|
||||
// an AND 4, press it. The kernel will deal with motor control subsequently.
|
||||
if(!play_button_) {
|
||||
const uint16_t pc = m6502_.value_of(CPU::MOS6502::Register::ProgramCounter);
|
||||
const uint8_t next[] = {
|
||||
map_.read(pc+0),
|
||||
map_.read(pc+1),
|
||||
map_.read(pc+2),
|
||||
map_.read(pc+3),
|
||||
};
|
||||
|
||||
// TODO: boil this down to a PC check. It's currently in this form as I'm unclear what
|
||||
// diversity of kernels exist.
|
||||
if(next[0] == 0x29 && next[1] == 0x04 && next[2] == 0xd0 && next[3] == 0xf4) {
|
||||
play_button_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
*value = 0xff ^ (play_button_ ? 0x4 :0x0);
|
||||
} else {
|
||||
*value = 0xff;
|
||||
}
|
||||
@ -286,33 +303,32 @@ public:
|
||||
case 0xff03: *value = timers_.read<3>(); break;
|
||||
case 0xff04: *value = timers_.read<4>(); break;
|
||||
case 0xff05: *value = timers_.read<5>(); break;
|
||||
|
||||
case 0xff08: *value = keyboard_latch_; break;
|
||||
|
||||
case 0xff09: *value = interrupts_.status(); break;
|
||||
case 0xff0a: *value = interrupts_.mask(); break;
|
||||
|
||||
case 0xff06: *value = video_.read<0xff06>(); break;
|
||||
case 0xff07: *value = video_.read<0xff07>(); break;
|
||||
case 0xff08: *value = keyboard_latch_; break;
|
||||
case 0xff09: *value = interrupts_.status(); break;
|
||||
case 0xff0a: *value = interrupts_.mask(); break;
|
||||
case 0xff0b: *value = video_.read<0xff0b>(); break;
|
||||
case 0xff1c: *value = video_.read<0xff1c>(); break;
|
||||
case 0xff1d: *value = video_.read<0xff1d>(); break;
|
||||
|
||||
case 0xff0c: *value = video_.read<0xff0c>(); break;
|
||||
case 0xff0d: *value = video_.read<0xff0d>(); break;
|
||||
case 0xff0e: *value = ff0e_; break;
|
||||
case 0xff0f: *value = ff0f_; break;
|
||||
case 0xff10: *value = ff10_; break;
|
||||
case 0xff11: *value = ff11_; break;
|
||||
case 0xff12: *value = ff12_; break;
|
||||
case 0xff13: *value = ff13_ | (rom_is_paged_ ? 1 : 0); break;
|
||||
|
||||
case 0xff0c: *value = video_.read<0xff0c>(); break;
|
||||
case 0xff0d: *value = video_.read<0xff0d>(); break;
|
||||
case 0xff14: *value = video_.read<0xff14>(); break;
|
||||
case 0xff15: *value = video_.read<0xff15>(); break;
|
||||
case 0xff16: *value = video_.read<0xff16>(); break;
|
||||
case 0xff17: *value = video_.read<0xff17>(); break;
|
||||
case 0xff18: *value = video_.read<0xff18>(); break;
|
||||
case 0xff19: *value = video_.read<0xff19>(); break;
|
||||
case 0xff1a: *value = video_.read<0xff1a>(); break;
|
||||
case 0xff1b: *value = video_.read<0xff1b>(); break;
|
||||
case 0xff1c: *value = video_.read<0xff1c>(); break;
|
||||
case 0xff1d: *value = video_.read<0xff1d>(); break;
|
||||
case 0xff1e: *value = video_.read<0xff1e>(); break;
|
||||
case 0xff1f: *value = video_.read<0xff1f>(); break;
|
||||
|
||||
default:
|
||||
printf("TODO: TED read at %04x\n", address);
|
||||
@ -325,7 +341,8 @@ public:
|
||||
case 0xff03: timers_.write<3>(*value); break;
|
||||
case 0xff04: timers_.write<4>(*value); break;
|
||||
case 0xff05: timers_.write<5>(*value); break;
|
||||
|
||||
case 0xff06: video_.write<0xff06>(*value); break;
|
||||
case 0xff07: video_.write<0xff07>(*value); break;
|
||||
case 0xff08:
|
||||
// Observation here: the kernel posts a 0 to this
|
||||
// address upon completing each keyboard scan cycle,
|
||||
@ -348,7 +365,6 @@ public:
|
||||
((*value & 0x80) ? 0x00 : key_states_[7])
|
||||
);
|
||||
break;
|
||||
|
||||
case 0xff09:
|
||||
interrupts_.set_status(*value);
|
||||
break;
|
||||
@ -357,9 +373,6 @@ public:
|
||||
video_.write<0xff0a>(*value);
|
||||
break;
|
||||
case 0xff0b: video_.write<0xff0b>(*value); break;
|
||||
|
||||
case 0xff06: video_.write<0xff06>(*value); break;
|
||||
case 0xff07: video_.write<0xff07>(*value); break;
|
||||
case 0xff0c: video_.write<0xff0c>(*value); break;
|
||||
case 0xff0d: video_.write<0xff0d>(*value); break;
|
||||
case 0xff0e:
|
||||
@ -400,14 +413,17 @@ public:
|
||||
video_.write<0xff13>(*value);
|
||||
break;
|
||||
case 0xff14: video_.write<0xff14>(*value); break;
|
||||
case 0xff1a: video_.write<0xff1a>(*value); break;
|
||||
case 0xff1b: video_.write<0xff1b>(*value); break;
|
||||
|
||||
case 0xff15: video_.write<0xff15>(*value); break;
|
||||
case 0xff16: video_.write<0xff16>(*value); break;
|
||||
case 0xff17: video_.write<0xff17>(*value); break;
|
||||
case 0xff18: video_.write<0xff18>(*value); break;
|
||||
case 0xff19: video_.write<0xff19>(*value); break;
|
||||
case 0xff1a: video_.write<0xff1a>(*value); break;
|
||||
case 0xff1b: video_.write<0xff1b>(*value); break;
|
||||
case 0xff1c: video_.write<0xff1c>(*value); break;
|
||||
case 0xff1d: video_.write<0xff1d>(*value); break;
|
||||
case 0xff1e: video_.write<0xff1e>(*value); break;
|
||||
case 0xff1f: video_.write<0xff1f>(*value); break;
|
||||
|
||||
case 0xff3e: page_cpu_rom(); break;
|
||||
case 0xff3f: page_cpu_ram(); break;
|
||||
@ -559,6 +575,7 @@ private:
|
||||
SerialPort serial_port_;
|
||||
|
||||
std::unique_ptr<Storage::Tape::BinaryTapePlayer> tape_player_;
|
||||
bool play_button_ = false;
|
||||
bool allow_fast_tape_hack_ = false; // TODO: implement fast-tape hack.
|
||||
void set_use_fast_tape() {}
|
||||
|
||||
|
@ -49,12 +49,19 @@ public:
|
||||
case 0xff0b: return uint8_t(raster_interrupt_);
|
||||
case 0xff0c: return cursor_position_ >> 8;
|
||||
case 0xff0d: return uint8_t(cursor_position_);
|
||||
case 0xff1c: return uint8_t(vertical_counter_ >> 8);
|
||||
case 0xff1d: return uint8_t(vertical_counter_);
|
||||
case 0xff14: return uint8_t((video_matrix_base_ >> 8) & 0xf8);
|
||||
|
||||
case 0xff15: case 0xff16: case 0xff17: case 0xff18: case 0xff19:
|
||||
return raw_background_[size_t(address - 0xff15)];
|
||||
|
||||
case 0xff1c: return uint8_t(vertical_counter_ >> 8);
|
||||
case 0xff1d: return uint8_t(vertical_counter_);
|
||||
|
||||
case 0xff1a:
|
||||
case 0xff1b:
|
||||
case 0xff1e:
|
||||
case 0xff1f:
|
||||
printf("TODO: TED video read at %04x\n", address);
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
@ -126,6 +133,16 @@ public:
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xff0a:
|
||||
raster_interrupt_ = (raster_interrupt_ & 0x00ff) | ((value & 1) << 8);
|
||||
break;
|
||||
case 0xff0b:
|
||||
raster_interrupt_ = (raster_interrupt_ & 0xff00) | value;
|
||||
break;
|
||||
|
||||
case 0xff0c: load_high10(cursor_position_); break;
|
||||
case 0xff0d: load_low8(cursor_position_); break;
|
||||
|
||||
case 0xff12:
|
||||
bitmap_base_ = uint16_t((value & 0x38) << 10);
|
||||
break;
|
||||
@ -137,22 +154,19 @@ public:
|
||||
video_matrix_base_ = uint16_t((value & 0xf8) << 8);
|
||||
break;
|
||||
|
||||
case 0xff0a:
|
||||
raster_interrupt_ = (raster_interrupt_ & 0x00ff) | ((value & 1) << 8);
|
||||
break;
|
||||
case 0xff0b:
|
||||
raster_interrupt_ = (raster_interrupt_ & 0xff00) | value;
|
||||
break;
|
||||
|
||||
case 0xff0c: load_high10(cursor_position_); break;
|
||||
case 0xff0d: load_low8(cursor_position_); break;
|
||||
case 0xff1a: load_high10(character_position_reload_); break;
|
||||
case 0xff1b: load_low8(character_position_reload_); break;
|
||||
|
||||
case 0xff15: case 0xff16: case 0xff17: case 0xff18: case 0xff19:
|
||||
raw_background_[size_t(address - 0xff15)] = value;
|
||||
background_[size_t(address - 0xff15)] = colour(value);
|
||||
break;
|
||||
|
||||
case 0xff1a: load_high10(character_position_reload_); break;
|
||||
case 0xff1b: load_low8(character_position_reload_); break;
|
||||
|
||||
case 0xff1c:
|
||||
case 0xff1d:
|
||||
case 0xff1e:
|
||||
case 0xff1f:
|
||||
printf("TODO: TED video write at %04x\n", address);
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,7 +343,11 @@ public:
|
||||
next_character_.advance();
|
||||
next_pixels_.advance();
|
||||
|
||||
output_.load_pixels(next_pixels_.read(), x_scroll_);
|
||||
const bool is_2bpp =
|
||||
(video_mode_ == VideoMode::MulticolourBitmap) ||
|
||||
(video_mode_ == VideoMode::MulticolourText && output_.attributes<0>() & 0x8);
|
||||
const int adjustment = (x_scroll_ & 1) && is_2bpp;
|
||||
output_.load_pixels(next_pixels_.read(), x_scroll_ + adjustment);
|
||||
}
|
||||
if(increment_video_counter_) {
|
||||
//
|
||||
@ -987,22 +1005,25 @@ private:
|
||||
template <int length, bool is_leftovers>
|
||||
void draw_2bpp_segment(uint16_t *const target, const uint16_t *colours) {
|
||||
constexpr int leftover = is_leftovers && (length & 1);
|
||||
static_assert(length + leftover <= 8);
|
||||
if(target) {
|
||||
const auto pixels = output_.pixels();
|
||||
// Intention: skip first output if leftover is 1, but still do the correct
|
||||
// length of output.
|
||||
if constexpr (!leftover && length >= 1) target[0] = colours[(pixels >> 6) & 3];
|
||||
if constexpr (length + leftover >= 2) target[1] = colours[(pixels >> 6) & 3];
|
||||
if constexpr (length + leftover >= 3) target[2] = colours[(pixels >> 4) & 3];
|
||||
if constexpr (length + leftover >= 4) target[3] = colours[(pixels >> 4) & 3];
|
||||
if constexpr (length + leftover >= 5) target[4] = colours[(pixels >> 2) & 3];
|
||||
if constexpr (length + leftover >= 6) target[5] = colours[(pixels >> 2) & 3];
|
||||
if constexpr (length + leftover >= 7) target[6] = colours[(pixels >> 0) & 3];
|
||||
if constexpr (length + leftover >= 8) target[7] = colours[(pixels >> 0) & 3];
|
||||
if constexpr (length + leftover >= 2) target[1 - leftover] = colours[(pixels >> 6) & 3];
|
||||
if constexpr (length + leftover >= 3) target[2 - leftover] = colours[(pixels >> 4) & 3];
|
||||
if constexpr (length + leftover >= 4) target[3 - leftover] = colours[(pixels >> 4) & 3];
|
||||
if constexpr (length + leftover >= 5) target[4 - leftover] = colours[(pixels >> 2) & 3];
|
||||
if constexpr (length + leftover >= 6) target[5 - leftover] = colours[(pixels >> 2) & 3];
|
||||
if constexpr (length + leftover >= 7) target[6 - leftover] = colours[(pixels >> 0) & 3];
|
||||
if constexpr (length + leftover >= 8) target[7 - leftover] = colours[(pixels >> 0) & 3];
|
||||
}
|
||||
|
||||
if(is_leftovers) {
|
||||
output_.advance_pixels(length + leftover);
|
||||
if constexpr (is_leftovers) {
|
||||
constexpr int shift_distance = length + leftover;
|
||||
static_assert(!(shift_distance&1));
|
||||
output_.advance_pixels(shift_distance);
|
||||
} else {
|
||||
output_.advance_pixels(length & ~1);
|
||||
}
|
||||
|
@ -185,7 +185,15 @@ static const int NumBuffers = MaximumBacklog + 1;
|
||||
// Starting is a no-op if the queue is already playing, but it may not have been started
|
||||
// yet, or may have been paused due to a pipeline failure if the producer is running slowly.
|
||||
if(enqueuedBuffers > 1) {
|
||||
OSSGuard(^{return AudioQueueStart(self->_audioQueue, NULL);});
|
||||
OSSGuard(^{
|
||||
const OSStatus result = AudioQueueStart(self->_audioQueue, NULL);
|
||||
if(result == kAudioQueueErr_CannotStart) {
|
||||
// Accept cannot-start, hoping it's ephemeral; Apple's specific advice is:
|
||||
// "Sleeping briefly and retrying is recommended".
|
||||
return kAudioSessionNoError;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
[_queueLock unlock];
|
||||
}
|
||||
|
@ -17,33 +17,44 @@ CommodoreTAP::CommodoreTAP(const std::string &file_name) : Tape(serialiser_), se
|
||||
CommodoreTAP::Serialiser::Serialiser(const std::string &file_name) :
|
||||
file_(file_name, FileHolder::FileMode::Read)
|
||||
{
|
||||
if(!file_.check_signature("C64-TAPE-RAW"))
|
||||
const bool is_c64 = file_.check_signature("C64-TAPE-RAW");
|
||||
file_.seek(0, SEEK_SET);
|
||||
const bool is_c16 = file_.check_signature("C16-TAPE-RAW");
|
||||
if(!is_c64 && !is_c16) {
|
||||
throw ErrorNotCommodoreTAP;
|
||||
}
|
||||
type_ = is_c16 ? FileType::C16 : FileType::C64;
|
||||
|
||||
// check the file version
|
||||
switch(file_.get8()) {
|
||||
case 0: updated_layout_ = false; break;
|
||||
case 1: updated_layout_ = true; break;
|
||||
default: throw ErrorNotCommodoreTAP;
|
||||
// Get and check the file version.
|
||||
version_ = file_.get8();
|
||||
if(version_ > 2) {
|
||||
throw ErrorNotCommodoreTAP;
|
||||
}
|
||||
|
||||
// skip reserved bytes
|
||||
file_.seek(3, SEEK_CUR);
|
||||
// Read clock rate-implying bytes.
|
||||
platform_ = Platform(file_.get8());
|
||||
video_ = VideoStandard(file_.get8());
|
||||
file_.seek(1, SEEK_CUR);
|
||||
|
||||
// read file size
|
||||
// Read file size.
|
||||
file_size_ = file_.get32le();
|
||||
|
||||
// set up for pulse output at the PAL clock rate, with each high and
|
||||
// low being half of whatever length values will be read; pretend that
|
||||
// a high pulse has just been distributed to imply that the next thing
|
||||
// that needs to happen is a length check
|
||||
current_pulse_.length.clock_rate = 985248 * 2;
|
||||
current_pulse_.type = Pulse::High;
|
||||
// Pick clock rate.
|
||||
current_pulse_.length.clock_rate = static_cast<unsigned int>(
|
||||
[&] {
|
||||
switch(platform_) {
|
||||
case Platform::Vic20: // It empirically seems like Vic-20 waves are counted with C64 timings?
|
||||
case Platform::C64: return video_ == VideoStandard::PAL ? 985'248 : 1'022'727;
|
||||
case Platform::C16: return video_ == VideoStandard::PAL ? 886'722 : 894'886;
|
||||
}
|
||||
}() * (double_clock() ? 2 : 1)
|
||||
);
|
||||
reset();
|
||||
}
|
||||
|
||||
void CommodoreTAP::Serialiser::reset() {
|
||||
file_.seek(0x14, SEEK_SET);
|
||||
current_pulse_.type = Pulse::High;
|
||||
current_pulse_.type = Pulse::High; // Implies that the first posted wave will be ::Low.
|
||||
is_at_end_ = false;
|
||||
}
|
||||
|
||||
@ -56,10 +67,10 @@ Storage::Tape::Pulse CommodoreTAP::Serialiser::next_pulse() {
|
||||
return current_pulse_;
|
||||
}
|
||||
|
||||
if(current_pulse_.type == Pulse::High) {
|
||||
const auto read_next_length = [&]() -> bool {
|
||||
uint32_t next_length;
|
||||
const uint8_t next_byte = file_.get8();
|
||||
if(!updated_layout_ || next_byte > 0) {
|
||||
if(!updated_layout() || next_byte > 0) {
|
||||
next_length = uint32_t(next_byte) << 3;
|
||||
} else {
|
||||
next_length = file_.get24le();
|
||||
@ -69,8 +80,19 @@ Storage::Tape::Pulse CommodoreTAP::Serialiser::next_pulse() {
|
||||
is_at_end_ = true;
|
||||
current_pulse_.length.length = current_pulse_.length.clock_rate;
|
||||
current_pulse_.type = Pulse::Zero;
|
||||
return false;
|
||||
} else {
|
||||
current_pulse_.length.length = next_length;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if(half_waves()) {
|
||||
if(read_next_length()) {
|
||||
current_pulse_.type = current_pulse_.type == Pulse::High ? Pulse::Low : Pulse::High;
|
||||
}
|
||||
} else if(current_pulse_.type == Pulse::High) {
|
||||
if(read_next_length()) {
|
||||
current_pulse_.type = Pulse::Low;
|
||||
}
|
||||
} else {
|
||||
|
@ -43,8 +43,30 @@ private:
|
||||
|
||||
Storage::FileHolder file_;
|
||||
|
||||
bool updated_layout_;
|
||||
uint32_t file_size_;
|
||||
enum class FileType {
|
||||
C16, C64,
|
||||
} type_;
|
||||
uint8_t version_;
|
||||
enum class Platform: uint8_t {
|
||||
C64 = 0,
|
||||
Vic20 = 1,
|
||||
C16 = 2,
|
||||
} platform_;
|
||||
enum class VideoStandard: uint8_t {
|
||||
PAL = 0,
|
||||
NTSC1 = 1,
|
||||
NTSC2 = 2,
|
||||
} video_;
|
||||
bool updated_layout() const {
|
||||
return version_ >= 1;
|
||||
}
|
||||
bool half_waves() const {
|
||||
return version_ >= 2;
|
||||
}
|
||||
bool double_clock() const {
|
||||
return platform_ != Platform::C16 || !half_waves();
|
||||
}
|
||||
|
||||
Pulse current_pulse_;
|
||||
bool is_at_end_ = false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user