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:
parent
50e23f4a2e
commit
f00f6c8c23
@ -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() {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user