1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Allows the frame interrupt to be placed anywhere in the frame.

This commit is contained in:
Thomas Harte 2018-10-10 21:07:39 -04:00
parent 50e23f4a2e
commit f00f6c8c23
2 changed files with 39 additions and 24 deletions

View File

@ -168,10 +168,6 @@ void TMS9918::run_for(const HalfCycles cycles) {
// PAL output is 313 lines total. NTSC output is 262 lines total.
// Interrupt is signalled upon entering the lower border.
// Keep a count of cycles separate from internal counts to avoid
// potential errors mapping back and forth.
half_cycles_into_frame_ = (half_cycles_into_frame_ + cycles) % HalfCycles(mode_timing_.total_lines * 228 * 2);
// Convert 456 clocked half cycles per line to 342 internal cycles per line;
// the internal clock is 1.5 times the nominal 3.579545 Mhz that I've advertised
// for this part. So multiply by three quarters.
@ -306,6 +302,9 @@ void TMS9918::run_for(const HalfCycles cycles) {
// -------------------------------
// Check for interrupt conditions.
// -------------------------------
if(column_ < mode_timing_.line_interrupt_position && end_column >= mode_timing_.line_interrupt_position) {
// The Sega VDP offers a decrementing counter for triggering line interrupts;
@ -327,10 +326,20 @@ void TMS9918::run_for(const HalfCycles cycles) {
// So life is easy.
}
if(
row_ == mode_timing_.end_of_frame_interrupt_position.row &&
column_ < mode_timing_.end_of_frame_interrupt_position.column &&
end_column >= mode_timing_.end_of_frame_interrupt_position.column
) {
status_ |= StatusInterrupt;
}
// -------------
// Advance time.
// -------------
column_ = end_column; // column_ is now the column that has been reached in this line.
int_cycles -= cycles_left; // Count down duration to run for.
@ -342,7 +351,6 @@ void TMS9918::run_for(const HalfCycles cycles) {
if(column_ == 342) {
column_ = 0;
row_ = (row_ + 1) % mode_timing_.total_lines;
if(row_ == mode_timing_.pixel_lines) status_ |= StatusInterrupt;
// Establish the output mode for the next line.
set_current_mode();
@ -530,15 +538,24 @@ uint8_t TMS9918::get_register(int address) {
return result;
}
HalfCycles TMS9918::get_time_until_interrupt() {
HalfCycles Base::half_cycles_before_internal_cycles(int internal_cycles) {
return HalfCycles(((internal_cycles << 2) - cycles_error_) / 3);
}
HalfCycles TMS9918::get_time_until_interrupt() {
if(!generate_interrupts_ && !enable_line_interrupts_) return HalfCycles(-1);
if(get_interrupt_line()) return HalfCycles(0);
// Calculate the amount of time until the next end-of-frame interrupt.
const int half_cycles_per_frame = mode_timing_.total_lines * 228 * 2;
const int half_cycles_remaining = (192 * 228 * 2 + half_cycles_per_frame - half_cycles_into_frame_.as_int()) % half_cycles_per_frame;
const auto time_until_frame_interrupt = HalfCycles(half_cycles_remaining ? half_cycles_remaining : half_cycles_per_frame);
if(!enable_line_interrupts_) return time_until_frame_interrupt;
const int frame_length = 342 * mode_timing_.pixel_lines;
const int time_until_frame_interrupt =
(
((mode_timing_.end_of_frame_interrupt_position.row * 342) + mode_timing_.end_of_frame_interrupt_position.column + frame_length) -
((row_ * 342) + column_)
) % frame_length;
if(!enable_line_interrupts_) return half_cycles_before_internal_cycles(time_until_frame_interrupt);
// Calculate the row upon which the next line interrupt will occur;
int next_line_interrupt_row = -1;
@ -558,25 +575,21 @@ uint8_t TMS9918::get_register(int address) {
// If there's actually no interrupt upcoming, despite being enabled, either return
// the frame end interrupt or no interrupt pending as appropriate.
if(next_line_interrupt_row == -1) {
return generate_interrupts_ ? time_until_frame_interrupt : HalfCycles(-1);
return generate_interrupts_ ?
half_cycles_before_internal_cycles(time_until_frame_interrupt) :
HalfCycles(-1);
}
// Figure out the number of internal cycles until the next line interrupt, which is the amount
// of time to the next tick over and then next_line_interrupt_row - row_ lines further.
int local_cycles_until_next_tick = (mode_timing_.line_interrupt_position - column_ + 342) % 342;
if(!local_cycles_until_next_tick) local_cycles_until_next_tick += 342;
const int local_cycles_until_line_interrupt = local_cycles_until_next_tick + (next_line_interrupt_row - row_) * 342;
int local_cycles_until_line_interrupt = local_cycles_until_next_tick + (next_line_interrupt_row - row_) * 342;
local_cycles_until_line_interrupt <<= 2;
local_cycles_until_line_interrupt -= cycles_error_;
// Map that to input cycles by multiplying by 2/3 (and incrementing on any carry, TODO: allowing for current error).
auto time_until_line_interrupt = HalfCycles(local_cycles_until_line_interrupt / 3);
if(!generate_interrupts_) return time_until_line_interrupt;
if(!generate_interrupts_) return half_cycles_before_internal_cycles(time_until_frame_interrupt);
// Return whichever interrupt is closer.
return std::min(time_until_frame_interrupt, time_until_line_interrupt);
return half_cycles_before_internal_cycles(std::min(local_cycles_until_line_interrupt, time_until_frame_interrupt));
}
bool TMS9918::get_interrupt_line() {

View File

@ -106,10 +106,9 @@ class Base {
uint8_t background_colour_ = 0;
// Internal mechanisms for position tracking.
HalfCycles half_cycles_into_frame_;
int column_ = 0, row_ = 0, output_column_ = 0;
int column_ = 0, row_ = 0;
int cycles_error_ = 0;
int access_pointer_ = 0;
HalfCycles half_cycles_before_internal_cycles(int internal_cycles);
// A helper function to output the current border colour for
// the number of cycles supplied.
@ -156,7 +155,10 @@ class Base {
// Set the position, in cycles, of the two interrupts,
// within a line.
int end_of_frame_interrupt_position = 342;
struct {
int column = 342;
int row = 191;
} end_of_frame_interrupt_position;
int line_interrupt_position = -1;
// Enables or disabled the recognition of 0xd0 as a sprite