diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index 14b334a22..3995d44e3 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -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() { diff --git a/Components/9918/Implementation/9918Base.hpp b/Components/9918/Implementation/9918Base.hpp index ee0a94ff8..479da6495 100644 --- a/Components/9918/Implementation/9918Base.hpp +++ b/Components/9918/Implementation/9918Base.hpp @@ -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