1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +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(0xc01e): VideoRead(get_alternative_character_set()); break;
case Read(0xc01f): VideoRead(get_80_columns()); break;
case Read(0xc046): VideoRead(get_annunciator_3()); break;
#undef VideoRead
#undef AuxiliaryRead
#undef LanguageRead
@ -483,24 +482,30 @@ class ConcreteMachine:
// // TODO: "Used during DMA as bank address"?
// break;
// case Read(0xc041): case Write(0xc041):
// // TODO: 'INTEN'; seems possibly to provide the same interrupt behaviour as a IIc?
// break;
// case Read(0xc044): case Write(0xc044):
// // TODO: MMDELTAX byte?
// break;
// case Read(0xc045): case Write(0xc045):
// // TODO: MMDELTAX byte?
// break;
// case Read(0xc046): case Write(0xc046):
// // TODO: DIAGTYPE byte? And INTFLAG?
// break;
// case Read(0xc047): case Write(0xc047):
// // TODO: Clear the VBL/3.75Hz interrupt flags (?)
// break;
// case Read(0xc048): case Write(0xc048):
// // TODO: Clear Mega II mouse interrupt flags
// break;
case Read(0xc041):
*value = megaii_interrupt_mask_;
break;
case Write(0xc041):
megaii_interrupt_mask_ = *value;
video_->set_megaii_interrupts_enabled(*value);
break;
case Read(0xc044):
// MMDELTAX byte.
*value = 0;
break;
case Read(0xc045):
// MMDELTAX byte.
*value = 0;
break;
case Read(0xc046):
*value = video_->get_megaii_interrupt_status();
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.
// b7, b6, b5: character generator language select;
@ -785,7 +790,7 @@ class ConcreteMachine:
void update_interrupts() {
// Update the interrupt line.
// 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:
@ -852,6 +857,8 @@ class ConcreteMachine:
bool test_mode_ = false;
uint8_t language_ = 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 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.
constexpr uint16_t double_bytes[128] = {
@ -146,10 +150,20 @@ void Video::set_internal_ram(const uint8_t *ram) {
}
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 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 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;
// 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(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.
return Cycles(CyclesPerLine + sequence_point_offset - cycles_into_row + (Lines - row - 1)*CyclesPerLine);
// Replace with the next Mega II interrupt point if those are enabled and it is sooner.
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) {
@ -478,6 +502,22 @@ uint8_t Video::get_interrupt_register() {
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() {
set_interrupts(interrupts_ | 0x40);
}

View File

@ -37,6 +37,7 @@ class Video: public Apple::II::VideoSwitches<Cycles> {
void clear_interrupts(uint8_t);
uint8_t get_interrupt_register();
void set_interrupt_register(uint8_t);
bool get_interrupt_line();
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.
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:
Outputs::CRT::CRT crt_;
@ -187,6 +196,11 @@ class Video: public Apple::II::VideoSwitches<Cycles> {
const int vertical, horizontal;
};
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.
};
}