1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-07 04:05:30 +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. // PAL output is 313 lines total. NTSC output is 262 lines total.
// Interrupt is signalled upon entering the lower border. // 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; // 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 // the internal clock is 1.5 times the nominal 3.579545 Mhz that I've advertised
// for this part. So multiply by three quarters. // 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) { 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; // 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. // 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. // Advance time.
// ------------- // -------------
column_ = end_column; // column_ is now the column that has been reached in this line. 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. int_cycles -= cycles_left; // Count down duration to run for.
@ -342,7 +351,6 @@ void TMS9918::run_for(const HalfCycles cycles) {
if(column_ == 342) { if(column_ == 342) {
column_ = 0; column_ = 0;
row_ = (row_ + 1) % mode_timing_.total_lines; row_ = (row_ + 1) % mode_timing_.total_lines;
if(row_ == mode_timing_.pixel_lines) status_ |= StatusInterrupt;
// Establish the output mode for the next line. // Establish the output mode for the next line.
set_current_mode(); set_current_mode();
@ -530,15 +538,24 @@ uint8_t TMS9918::get_register(int address) {
return result; return result;
} }
HalfCycles Base::half_cycles_before_internal_cycles(int internal_cycles) {
return HalfCycles(((internal_cycles << 2) - cycles_error_) / 3);
}
HalfCycles TMS9918::get_time_until_interrupt() { HalfCycles TMS9918::get_time_until_interrupt() {
if(!generate_interrupts_ && !enable_line_interrupts_) return HalfCycles(-1); if(!generate_interrupts_ && !enable_line_interrupts_) return HalfCycles(-1);
if(get_interrupt_line()) return HalfCycles(0); if(get_interrupt_line()) return HalfCycles(0);
// Calculate the amount of time until the next end-of-frame interrupt. // 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 frame_length = 342 * mode_timing_.pixel_lines;
const int half_cycles_remaining = (192 * 228 * 2 + half_cycles_per_frame - half_cycles_into_frame_.as_int()) % half_cycles_per_frame; const int time_until_frame_interrupt =
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; ((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; // Calculate the row upon which the next line interrupt will occur;
int next_line_interrupt_row = -1; 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 // If there's actually no interrupt upcoming, despite being enabled, either return
// the frame end interrupt or no interrupt pending as appropriate. // the frame end interrupt or no interrupt pending as appropriate.
if(next_line_interrupt_row == -1) { 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 // 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. // 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; 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; 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; if(!generate_interrupts_) return half_cycles_before_internal_cycles(time_until_frame_interrupt);
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;
// Return whichever interrupt is closer. // 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() { bool TMS9918::get_interrupt_line() {

View File

@ -106,10 +106,9 @@ class Base {
uint8_t background_colour_ = 0; uint8_t background_colour_ = 0;
// Internal mechanisms for position tracking. // Internal mechanisms for position tracking.
HalfCycles half_cycles_into_frame_; int column_ = 0, row_ = 0;
int column_ = 0, row_ = 0, output_column_ = 0;
int cycles_error_ = 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 // A helper function to output the current border colour for
// the number of cycles supplied. // the number of cycles supplied.
@ -156,7 +155,10 @@ class Base {
// Set the position, in cycles, of the two interrupts, // Set the position, in cycles, of the two interrupts,
// within a line. // 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; int line_interrupt_position = -1;
// Enables or disabled the recognition of 0xd0 as a sprite // Enables or disabled the recognition of 0xd0 as a sprite