1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 16:31:31 +00:00

Merge pull request #559 from TomHarte/RowPhase

Corrects row reporting for modes other than 192-line NTSC
This commit is contained in:
Thomas Harte 2018-10-20 18:28:26 -04:00 committed by GitHub
commit a38974ef2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 28 deletions

View File

@ -76,9 +76,9 @@ Base::Base(Personality p) :
// This is definitely correct for the TMS; more research may be
// necessary for the other implemented VDPs.
read_pointer_.row = 0;
read_pointer_.column = 0;
write_pointer_.row = 0;
write_pointer_.column = 10; // i.e. 10 cycles ahead of the read pointer.
read_pointer_.column = 342 - 10; // i.e. 10 cycles behind the write pointer.
write_pointer_.row = 1;
write_pointer_.column = 0;
}
TMS9918::TMS9918(Personality p):
@ -102,6 +102,7 @@ TMS9918::TMS9918(Personality p):
}
void TMS9918::set_tv_standard(TVStandard standard) {
tv_standard_ = standard;
switch(standard) {
case TVStandard::PAL:
mode_timing_.total_lines = 313;
@ -569,22 +570,30 @@ uint8_t TMS9918::get_current_line() {
? (write_pointer_.row + mode_timing_.total_lines - 1)%mode_timing_.total_lines
: write_pointer_.row;
// This assumes NTSC 192-line. TODO: other modes.
if(source_row >= 0xdb) source_row -= 6;
// printf("Current row: %d -> %d\n", row_, source_row);
if(tv_standard_ == TVStandard::NTSC) {
if(mode_timing_.pixel_lines == 240) {
// NTSC 256x240: 00-FF, 00-06
} else if(mode_timing_.pixel_lines == 224) {
// NTSC 256x224: 00-EA, E5-FF
if(source_row >= 0xeb) source_row -= 6;
} else {
// NTSC 256x192: 00-DA, D5-FF
if(source_row >= 0xdb) source_row -= 6;
}
} else {
if(mode_timing_.pixel_lines == 240) {
// PAL 256x240: 00-FF, 00-0A, D2-FF
if(source_row >= 267) source_row -= 0x39;
} else if(mode_timing_.pixel_lines == 224) {
// PAL 256x224: 00-FF, 00-02, CA-FF
if(source_row >= 259) source_row -= 0x39;
} else {
// PAL 256x192: 00-F2, BA-FF
if(source_row >= 0xf3) source_row -= 0x39;
}
}
return static_cast<uint8_t>(source_row);
/*
TODO: Full proper sequence of current lines:
NTSC 256x192 00-DA, D5-FF
NTSC 256x224 00-EA, E5-FF
NTSC 256x240 00-FF, 00-06
PAL 256x192 00-F2, BA-FF
PAL 256x224 00-FF, 00-02, CA-FF
PAL 256x240 00-FF, 00-0A, D2-FF
*/
}
uint8_t TMS9918::get_latched_horizontal_counter() {
@ -620,7 +629,7 @@ uint8_t TMS9918::get_register(int address) {
}
HalfCycles Base::half_cycles_before_internal_cycles(int internal_cycles) {
return HalfCycles(((internal_cycles << 2) - cycles_error_) / 3);
return HalfCycles(((internal_cycles << 2) + (2 - cycles_error_)) / 3);
}
HalfCycles TMS9918::get_time_until_interrupt() {
@ -637,7 +646,7 @@ HalfCycles TMS9918::get_time_until_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 the row upon which the next line interrupt will occur.
int next_line_interrupt_row = -1;
if(is_sega_vdp(personality_)) {
@ -666,7 +675,7 @@ HalfCycles TMS9918::get_time_until_interrupt() {
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(time_until_frame_interrupt);
if(!generate_interrupts_) return half_cycles_before_internal_cycles(local_cycles_until_line_interrupt);
// Return whichever interrupt is closer.
return half_cycles_before_internal_cycles(std::min(local_cycles_until_line_interrupt, time_until_frame_interrupt));

View File

@ -38,13 +38,6 @@ class TMS9918: public Base {
*/
TMS9918(Personality p);
enum TVStandard {
/*! i.e. 50Hz output at around 312.5 lines/field */
PAL,
/*! i.e. 60Hz output at around 262.5 lines/field */
NTSC
};
/*! Sets the TV standard for this TMS, if that is hard-coded in hardware. */
void set_tv_standard(TVStandard standard);

View File

@ -27,6 +27,13 @@ enum Personality {
GGVDP,
};
enum class TVStandard {
/*! i.e. 50Hz output at around 312.5 lines/field */
PAL,
/*! i.e. 60Hz output at around 262.5 lines/field */
NTSC
};
#define is_sega_vdp(x) x >= SMSVDP
class Base {
@ -69,6 +76,7 @@ class Base {
Personality personality_;
std::unique_ptr<Outputs::CRT::CRT> crt_;
TVStandard tv_standard_ = TVStandard::NTSC;
// Holds the contents of this VDP's connected DRAM.
std::vector<uint8_t> ram_;

View File

@ -149,7 +149,7 @@ class ConcreteMachine:
vdp_.reset(new TI::TMS::TMS9918(model_ == Target::Model::SG1000 ? TI::TMS::TMS9918A : TI::TMS::SMSVDP));
vdp_->set_tv_standard(
(region_ == Target::Region::Europe) ?
TI::TMS::TMS9918::TVStandard::PAL : TI::TMS::TMS9918::TVStandard::NTSC);
TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC);
get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite);
}

View File

@ -57,6 +57,10 @@
// Get time until interrupt.
int time_until_interrupt = vdp.get_time_until_interrupt().as_int() - 1;
// Check that an interrupt is now scheduled.
NSAssert(time_until_interrupt != -2, @"No interrupt scheduled");
NSAssert(time_until_interrupt > 0, @"Interrupt is scheduled in the past");
// Check interrupt flag isn't set prior to the reported time.
vdp.run_for(HalfCycles(time_until_interrupt));
NSAssert(!vdp.get_interrupt_line(), @"Interrupt line went active early [1]");
@ -79,4 +83,37 @@
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised [2]");
}
- (void)testFirstLineInterrupt {
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
// Disable end-of-frame interrupts, enable line interrupts, set an interrupt to occur every line.
vdp.set_register(1, 0x00);
vdp.set_register(1, 0x81);
vdp.set_register(1, 0x10);
vdp.set_register(1, 0x80);
vdp.set_register(1, 0);
vdp.set_register(1, 0x8a);
// Advance to outside of the counted area.
while(vdp.get_current_line() < 200) vdp.run_for(Cycles(228));
// Clear the pending interrupt and ask about the next one (i.e. the first one).
vdp.get_register(1);
int time_until_interrupt = vdp.get_time_until_interrupt().as_int() - 1;
// Check that an interrupt is now scheduled.
NSAssert(time_until_interrupt != -2, @"No interrupt scheduled");
NSAssert(time_until_interrupt > 0, @"Interrupt is scheduled in the past");
// Check interrupt flag isn't set prior to the reported time.
vdp.run_for(HalfCycles(time_until_interrupt));
NSAssert(!vdp.get_interrupt_line(), @"Interrupt line went active early");
// Check interrupt flag is set at the reported time.
vdp.run_for(HalfCycles(1));
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised");
}
@end