mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 18:31:53 +00:00
Merge pull request #562 from TomHarte/TimingTweaks
Corrects residual Master System interrupt timing issues.
This commit is contained in:
commit
fb3171f366
@ -66,7 +66,7 @@ Base::Base(Personality p) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(is_sega_vdp(personality_)) {
|
if(is_sega_vdp(personality_)) {
|
||||||
mode_timing_.line_interrupt_position = 65;
|
mode_timing_.line_interrupt_position = 64;
|
||||||
|
|
||||||
mode_timing_.end_of_frame_interrupt_position.column = 63;
|
mode_timing_.end_of_frame_interrupt_position.column = 63;
|
||||||
mode_timing_.end_of_frame_interrupt_position.row = 193;
|
mode_timing_.end_of_frame_interrupt_position.row = 193;
|
||||||
@ -564,7 +564,7 @@ void TMS9918::set_register(int address, uint8_t value) {
|
|||||||
|
|
||||||
uint8_t TMS9918::get_current_line() {
|
uint8_t TMS9918::get_current_line() {
|
||||||
// Determine the row to return.
|
// Determine the row to return.
|
||||||
static const int row_change_position = 62; // This is the proper Master System value; substitute if any other VDPs turn out to have this functionality.
|
static const int row_change_position = 63; // This is the proper Master System value; substitute if any other VDPs turn out to have this functionality.
|
||||||
int source_row =
|
int source_row =
|
||||||
(write_pointer_.column < row_change_position)
|
(write_pointer_.column < row_change_position)
|
||||||
? (write_pointer_.row + mode_timing_.total_lines - 1)%mode_timing_.total_lines
|
? (write_pointer_.row + mode_timing_.total_lines - 1)%mode_timing_.total_lines
|
||||||
@ -638,23 +638,31 @@ HalfCycles TMS9918::get_time_until_interrupt() {
|
|||||||
|
|
||||||
// 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 frame_length = 342 * mode_timing_.total_lines;
|
const int frame_length = 342 * mode_timing_.total_lines;
|
||||||
const int time_until_frame_interrupt =
|
int time_until_frame_interrupt =
|
||||||
(
|
(
|
||||||
((mode_timing_.end_of_frame_interrupt_position.row * 342) + mode_timing_.end_of_frame_interrupt_position.column + frame_length) -
|
((mode_timing_.end_of_frame_interrupt_position.row * 342) + mode_timing_.end_of_frame_interrupt_position.column + frame_length) -
|
||||||
((write_pointer_.row * 342) + write_pointer_.column)
|
((write_pointer_.row * 342) + write_pointer_.column)
|
||||||
) % frame_length;
|
) % frame_length;
|
||||||
|
if(!time_until_frame_interrupt) time_until_frame_interrupt = frame_length;
|
||||||
|
|
||||||
if(!enable_line_interrupts_) return half_cycles_before_internal_cycles(time_until_frame_interrupt);
|
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 when the next line interrupt will occur.
|
||||||
int next_line_interrupt_row = -1;
|
int next_line_interrupt_row = -1;
|
||||||
|
|
||||||
|
int cycles_to_next_interrupt_threshold = mode_timing_.line_interrupt_position - write_pointer_.column;
|
||||||
|
int line_of_next_interrupt_threshold = write_pointer_.row;
|
||||||
|
if(cycles_to_next_interrupt_threshold <= 0) {
|
||||||
|
cycles_to_next_interrupt_threshold += 342;
|
||||||
|
++line_of_next_interrupt_threshold;
|
||||||
|
}
|
||||||
|
|
||||||
if(is_sega_vdp(personality_)) {
|
if(is_sega_vdp(personality_)) {
|
||||||
// If there is still time for a line interrupt this frame, that'll be it;
|
// If there is still time for a line interrupt this frame, that'll be it;
|
||||||
// otherwise it'll be on the next frame, supposing there's ever time for
|
// otherwise it'll be on the next frame, supposing there's ever time for
|
||||||
// it at all.
|
// it at all.
|
||||||
if(write_pointer_.row+line_interrupt_counter <= mode_timing_.pixel_lines) {
|
if(line_of_next_interrupt_threshold + line_interrupt_counter <= mode_timing_.pixel_lines) {
|
||||||
next_line_interrupt_row = write_pointer_.row+line_interrupt_counter;
|
next_line_interrupt_row = line_of_next_interrupt_threshold + line_interrupt_counter;
|
||||||
} else {
|
} else {
|
||||||
if(line_interrupt_target <= mode_timing_.pixel_lines)
|
if(line_interrupt_target <= mode_timing_.pixel_lines)
|
||||||
next_line_interrupt_row = mode_timing_.total_lines + line_interrupt_target;
|
next_line_interrupt_row = mode_timing_.total_lines + line_interrupt_target;
|
||||||
@ -671,10 +679,7 @@ HalfCycles TMS9918::get_time_until_interrupt() {
|
|||||||
|
|
||||||
// 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 - write_pointer_.column + 342) % 342;
|
const int local_cycles_until_line_interrupt = cycles_to_next_interrupt_threshold + (next_line_interrupt_row - line_of_next_interrupt_threshold) * 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 - write_pointer_.row) * 342;
|
|
||||||
|
|
||||||
if(!generate_interrupts_) return half_cycles_before_internal_cycles(local_cycles_until_line_interrupt);
|
if(!generate_interrupts_) return half_cycles_before_internal_cycles(local_cycles_until_line_interrupt);
|
||||||
|
|
||||||
// Return whichever interrupt is closer.
|
// Return whichever interrupt is closer.
|
||||||
@ -687,9 +692,6 @@ bool TMS9918::get_interrupt_line() {
|
|||||||
|
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
|
||||||
// if(sprite.shift_position > 0 && !sprites_magnified_)
|
|
||||||
// sprite.shift_position *= 2;
|
|
||||||
|
|
||||||
void Base::draw_tms_character(int start, int end) {
|
void Base::draw_tms_character(int start, int end) {
|
||||||
LineBuffer &line_buffer = line_buffers_[read_pointer_.row];
|
LineBuffer &line_buffer = line_buffers_[read_pointer_.row];
|
||||||
|
|
||||||
|
@ -116,4 +116,45 @@
|
|||||||
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised");
|
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testPrediction {
|
||||||
|
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
|
||||||
|
|
||||||
|
for(int c = 0; c < 256; ++c) {
|
||||||
|
for(int with_eof = (c < 192) ? 0 : 1; with_eof < 2; ++with_eof) {
|
||||||
|
// Enable or disable end-of-frame interrupts as required.
|
||||||
|
vdp.set_register(1, with_eof ? 0x20 : 0x00);
|
||||||
|
vdp.set_register(1, 0x81);
|
||||||
|
|
||||||
|
// Enable line interrupts.
|
||||||
|
vdp.set_register(1, 0x10);
|
||||||
|
vdp.set_register(1, 0x80);
|
||||||
|
|
||||||
|
// Set the line interrupt timing as desired.
|
||||||
|
vdp.set_register(1, c);
|
||||||
|
vdp.set_register(1, 0x8a);
|
||||||
|
|
||||||
|
// Now run through an entire frame...
|
||||||
|
int half_cycles = 262*224*2;
|
||||||
|
int last_time_until_interrupt = vdp.get_time_until_interrupt().as_int();
|
||||||
|
while(half_cycles--) {
|
||||||
|
// Validate that an interrupt happened if one was expected, and clear anything that's present.
|
||||||
|
NSAssert(vdp.get_interrupt_line() == (last_time_until_interrupt == 0), @"Unexpected interrupt state change; expected %d but got %d; position %d %d @ %d", (last_time_until_interrupt == 0), vdp.get_interrupt_line(), c, with_eof, half_cycles);
|
||||||
|
vdp.get_register(1);
|
||||||
|
|
||||||
|
vdp.run_for(HalfCycles(1));
|
||||||
|
|
||||||
|
// Get the time until interrupt.
|
||||||
|
int time_until_interrupt = vdp.get_time_until_interrupt().as_int();
|
||||||
|
NSAssert(time_until_interrupt != -1, @"No interrupt scheduled; position %d %d @ %d", c, with_eof, half_cycles);
|
||||||
|
NSAssert(time_until_interrupt >= 0, @"Interrupt is scheduled in the past; position %d %d @ %d", c, with_eof, half_cycles);
|
||||||
|
|
||||||
|
if(last_time_until_interrupt) {
|
||||||
|
NSAssert(time_until_interrupt == (last_time_until_interrupt - 1), @"Discontinuity found in interrupt prediction; from %d to %d; position %d %d @ %d", last_time_until_interrupt, time_until_interrupt, c, with_eof, half_cycles);
|
||||||
|
}
|
||||||
|
last_time_until_interrupt = time_until_interrupt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
Loading…
Reference in New Issue
Block a user