1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-19 23:29:05 +00:00

Takes a shot at the Mega II-style video interrupts.

This commit is contained in:
Thomas Harte 2020-11-29 21:21:46 -05:00
parent 3da6b4709c
commit 4bdbca64b2
3 changed files with 86 additions and 25 deletions

View File

@ -316,7 +316,6 @@ class ConcreteMachine:
case Read(0xc01d): VideoRead(get_high_resolution()); break; case Read(0xc01d): VideoRead(get_high_resolution()); break;
case Read(0xc01e): VideoRead(get_alternative_character_set()); break; case Read(0xc01e): VideoRead(get_alternative_character_set()); break;
case Read(0xc01f): VideoRead(get_80_columns()); break; case Read(0xc01f): VideoRead(get_80_columns()); break;
case Read(0xc046): VideoRead(get_annunciator_3()); break;
#undef VideoRead #undef VideoRead
#undef AuxiliaryRead #undef AuxiliaryRead
#undef LanguageRead #undef LanguageRead
@ -483,24 +482,30 @@ class ConcreteMachine:
// // TODO: "Used during DMA as bank address"? // // TODO: "Used during DMA as bank address"?
// break; // break;
// case Read(0xc041): case Write(0xc041): case Read(0xc041):
// // TODO: 'INTEN'; seems possibly to provide the same interrupt behaviour as a IIc? *value = megaii_interrupt_mask_;
// break; break;
// case Read(0xc044): case Write(0xc044): case Write(0xc041):
// // TODO: MMDELTAX byte? megaii_interrupt_mask_ = *value;
// break; video_->set_megaii_interrupts_enabled(*value);
// case Read(0xc045): case Write(0xc045): break;
// // TODO: MMDELTAX byte? case Read(0xc044):
// break; // MMDELTAX byte.
// case Read(0xc046): case Write(0xc046): *value = 0;
// // TODO: DIAGTYPE byte? And INTFLAG? break;
// break; case Read(0xc045):
// case Read(0xc047): case Write(0xc047): // MMDELTAX byte.
// // TODO: Clear the VBL/3.75Hz interrupt flags (?) *value = 0;
// break; break;
// case Read(0xc048): case Write(0xc048): case Read(0xc046):
// // TODO: Clear Mega II mouse interrupt flags *value = video_->get_megaii_interrupt_status();
// break; break;
case Read(0xc047): case Write(0xc047):
video_->clear_megaii_interrupts();
break;
case Read(0xc048): case Write(0xc048):
// No-op: Clear Mega II mouse interrupt flags
break;
// Language select. // Language select.
// b7, b6, b5: character generator language select; // b7, b6, b5: character generator language select;
@ -785,7 +790,7 @@ class ConcreteMachine:
void update_interrupts() { void update_interrupts() {
// Update the interrupt line. // Update the interrupt line.
// TODO: are there other interrupt sources? // TODO: are there other interrupt sources?
m65816_.set_irq_line((video_.last_valid()->get_interrupt_register() & 0x80) || sound_glu_.get_interrupt_line()); m65816_.set_irq_line(video_.last_valid()->get_interrupt_line() || sound_glu_.get_interrupt_line());
} }
private: private:
@ -852,6 +857,8 @@ class ConcreteMachine:
bool test_mode_ = false; bool test_mode_ = false;
uint8_t language_ = 0; uint8_t language_ = 0;
uint8_t disk_select_ = 0; uint8_t disk_select_ = 0;
uint8_t megaii_interrupt_mask_ = 0;
}; };
} }

View File

@ -80,6 +80,10 @@ constexpr int start_of_right_border = start_of_pixels + pixel_ticks;
constexpr int start_of_sync = start_of_right_border + right_border_ticks; constexpr int start_of_sync = start_of_right_border + right_border_ticks;
constexpr int sync_period = CyclesPerLine - start_of_sync*CyclesPerTick; constexpr int sync_period = CyclesPerLine - start_of_sync*CyclesPerTick;
// I have made the guess that this occurs when the Mega II horizontal counter rolls over.
// This is just a guess.
constexpr int megaii_interrupt_point = 192*CyclesPerLine + (start_of_pixels - 28)*CyclesPerTick - 2;
// A table to map from 7-bit integers to 14-bit versions with all bits doubled. // A table to map from 7-bit integers to 14-bit versions with all bits doubled.
constexpr uint16_t double_bytes[128] = { constexpr uint16_t double_bytes[128] = {
@ -146,10 +150,20 @@ void Video::set_internal_ram(const uint8_t *ram) {
} }
void Video::advance(Cycles cycles) { void Video::advance(Cycles cycles) {
const int next_cycles_into_frame = cycles_into_frame_ + cycles.as<int>();
// Check for Mega II-style interrupt sources, prior to updating cycles_into_frame_.
if(cycles_into_frame_ < megaii_interrupt_point && next_cycles_into_frame >= megaii_interrupt_point) {
++megaii_frame_counter_;
megaii_interrupt_state_ |= 0x08 | ((megaii_frame_counter_ / 15) * 0x10);
megaii_frame_counter_ %= 15;
}
// Update video output.
const int column_start = (cycles_into_frame_ % CyclesPerLine) / CyclesPerTick; const int column_start = (cycles_into_frame_ % CyclesPerLine) / CyclesPerTick;
const int row_start = cycles_into_frame_ / CyclesPerLine; const int row_start = cycles_into_frame_ / CyclesPerLine;
cycles_into_frame_ = (cycles_into_frame_ + cycles.as<int>()) % (CyclesPerLine * Lines); cycles_into_frame_ = next_cycles_into_frame % (CyclesPerLine * Lines);
const int column_end = (cycles_into_frame_ % CyclesPerLine) / CyclesPerTick; const int column_end = (cycles_into_frame_ % CyclesPerLine) / CyclesPerTick;
const int row_end = cycles_into_frame_ / CyclesPerLine; const int row_end = cycles_into_frame_ / CyclesPerLine;
@ -177,14 +191,24 @@ Cycles Video::get_next_sequence_point() const {
constexpr int sequence_point_offset = (blank_ticks + left_border_ticks) * CyclesPerTick; constexpr int sequence_point_offset = (blank_ticks + left_border_ticks) * CyclesPerTick;
// Handle every case that doesn't involve wrapping to the next row 0. // Seed as the distance ot the next row 0.
int result = CyclesPerLine + sequence_point_offset - cycles_into_row + (Lines - row - 1)*CyclesPerLine;
// Replace with the start of the next line, if closer.
if(row <= 200) { if(row <= 200) {
if(cycles_into_row < sequence_point_offset) return Cycles(sequence_point_offset - cycles_into_row); if(cycles_into_row < sequence_point_offset) return Cycles(sequence_point_offset - cycles_into_row);
if(row < 200) return Cycles(CyclesPerLine + sequence_point_offset - cycles_into_row); if(row < 200) result = CyclesPerLine + sequence_point_offset - cycles_into_row;
} }
// Calculate distance to the relevant point in row 0. // Replace with the next Mega II interrupt point if those are enabled and it is sooner.
return Cycles(CyclesPerLine + sequence_point_offset - cycles_into_row + (Lines - row - 1)*CyclesPerLine); if(megaii_interrupt_mask_) {
const int time_until_megaii = megaii_interrupt_point - cycles_into_frame_;
if(time_until_megaii > 0 && time_until_megaii < result) {
result = time_until_megaii;
}
}
return Cycles(result);
} }
void Video::output_row(int row, int start, int end) { void Video::output_row(int row, int start, int end) {
@ -478,6 +502,22 @@ uint8_t Video::get_interrupt_register() {
return interrupts_; return interrupts_;
} }
bool Video::get_interrupt_line() {
return (interrupts_&0x80) || (megaii_interrupt_mask_&megaii_interrupt_state_);
}
void Video::set_megaii_interrupts_enabled(uint8_t mask) {
megaii_interrupt_mask_ = mask;
}
uint8_t Video::get_megaii_interrupt_status() {
return megaii_interrupt_state_ | (get_annunciator_3() ? 0x20 : 0x00);
}
void Video::clear_megaii_interrupts() {
megaii_interrupt_state_ = 0;
}
void Video::notify_clock_tick() { void Video::notify_clock_tick() {
set_interrupts(interrupts_ | 0x40); set_interrupts(interrupts_ | 0x40);
} }

View File

@ -37,6 +37,7 @@ class Video: public Apple::II::VideoSwitches<Cycles> {
void clear_interrupts(uint8_t); void clear_interrupts(uint8_t);
uint8_t get_interrupt_register(); uint8_t get_interrupt_register();
void set_interrupt_register(uint8_t); void set_interrupt_register(uint8_t);
bool get_interrupt_line();
void notify_clock_tick(); void notify_clock_tick();
@ -62,6 +63,14 @@ class Video: public Apple::II::VideoSwitches<Cycles> {
/// Determines the period until video might autonomously update its interrupt lines. /// Determines the period until video might autonomously update its interrupt lines.
Cycles get_next_sequence_point() const; Cycles get_next_sequence_point() const;
/// Sets the Mega II interrupt enable state — 1/4-second and VBL interrupts are
/// generated here.
void set_megaii_interrupts_enabled(uint8_t);
uint8_t get_megaii_interrupt_status();
void clear_megaii_interrupts();
private: private:
Outputs::CRT::CRT crt_; Outputs::CRT::CRT crt_;
@ -187,6 +196,11 @@ class Video: public Apple::II::VideoSwitches<Cycles> {
const int vertical, horizontal; const int vertical, horizontal;
}; };
Counters get_counters(Cycles offset); Counters get_counters(Cycles offset);
// Marshalls the Mega II-style interrupt state.
uint8_t megaii_interrupt_mask_ = 0;
uint8_t megaii_interrupt_state_ = 0;
int megaii_frame_counter_ = 0; // To count up to quarter-second interrupts.
}; };
} }