mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Update ClockConverter for potential alternative clocks.
This commit is contained in:
parent
dc3f8f5e42
commit
475440dc70
@ -274,24 +274,21 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
|
|||||||
// ------------------------
|
// ------------------------
|
||||||
// Perform memory accesses.
|
// Perform memory accesses.
|
||||||
// ------------------------
|
// ------------------------
|
||||||
#define fetch(function) \
|
#define fetch(function, converter) \
|
||||||
if(final_window != this->clock_converter_.AccessWindowCyclesPerLine) { \
|
const int first_window = this->clock_converter_.converter(this->write_pointer_.column); \
|
||||||
|
const int final_window = this->clock_converter_.converter(end_column); \
|
||||||
|
if(first_window != final_window) break; \
|
||||||
|
if(final_window != TMSAccessWindowsPerLine) { \
|
||||||
function<true>(first_window, final_window); \
|
function<true>(first_window, final_window); \
|
||||||
} else { \
|
} else { \
|
||||||
function<false>(first_window, final_window); \
|
function<false>(first_window, final_window); \
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust column_ and end_column to the access-window clock before calling
|
|
||||||
// the mode-applicable fetch function.
|
|
||||||
const int first_window = this->clock_converter_.to_access_clock(this->write_pointer_.column);
|
|
||||||
const int final_window = this->clock_converter_.to_access_clock(end_column);
|
|
||||||
if(first_window != final_window) {
|
|
||||||
switch(line_buffer.line_mode) {
|
switch(line_buffer.line_mode) {
|
||||||
case LineMode::Text: fetch(this->template fetch_tms_text); break;
|
case LineMode::Text: { fetch(this->template fetch_tms_text, to_tms_access_clock); } break;
|
||||||
case LineMode::Character: fetch(this->template fetch_tms_character); break;
|
case LineMode::Character: { fetch(this->template fetch_tms_character, to_tms_access_clock); } break;
|
||||||
case LineMode::SMS: fetch(this->template fetch_sms); break;
|
case LineMode::SMS: { fetch(this->template fetch_sms, to_tms_access_clock); } break;
|
||||||
case LineMode::Refresh: fetch(this->template fetch_tms_refresh); break;
|
case LineMode::Refresh: { fetch(this->template fetch_tms_refresh, to_tms_access_clock); } break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef fetch
|
#undef fetch
|
||||||
|
@ -14,6 +14,17 @@
|
|||||||
namespace TI {
|
namespace TI {
|
||||||
namespace TMS {
|
namespace TMS {
|
||||||
|
|
||||||
|
template <Personality personality> constexpr int cycles_per_line() {
|
||||||
|
switch(personality) {
|
||||||
|
default: return 342;
|
||||||
|
case Personality::V9938:
|
||||||
|
case Personality::V9958: return 1368;
|
||||||
|
case Personality::MDVDP: return 3420;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int TMSAccessWindowsPerLine = 171;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
This implementation of the TMS, etc mediates between three clocks:
|
This implementation of the TMS, etc mediates between three clocks:
|
||||||
|
|
||||||
@ -47,38 +58,83 @@ template <Personality personality> class ClockConverter {
|
|||||||
Given that another @c source external **half-cycles** has occurred,
|
Given that another @c source external **half-cycles** has occurred,
|
||||||
indicates how many complete internal **cycles** have additionally elapsed
|
indicates how many complete internal **cycles** have additionally elapsed
|
||||||
since the last call to @c to_internal.
|
since the last call to @c to_internal.
|
||||||
|
|
||||||
|
E.g. for the TMS, @c source will count 456 ticks per line, and the internal clock
|
||||||
|
runs at 342 ticks per line, so the proper conversion is to multiply by 3/4.
|
||||||
*/
|
*/
|
||||||
int to_internal(int source) {
|
int to_internal(int source) {
|
||||||
// Default behaviour is top apply a multiplication by 3/4.
|
switch(personality) {
|
||||||
|
// Default behaviour is to apply a multiplication by 3/4;
|
||||||
|
// this is correct for the TMS and Sega VDPs other than the Mega Drive.
|
||||||
|
default: {
|
||||||
const int result = source * 3 + cycles_error_;
|
const int result = source * 3 + cycles_error_;
|
||||||
cycles_error_ = result & 3;
|
cycles_error_ = result & 3;
|
||||||
return result >> 2;
|
return result >> 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The two Yamaha chips have an internal clock that is four times
|
||||||
|
// as fast as the TMS, therefore a stateless translation is possible.
|
||||||
|
case Personality::V9938:
|
||||||
|
case Personality::V9958:
|
||||||
|
return source * 3;
|
||||||
|
|
||||||
|
// The Mega Drive runs at 3420 master clocks per line, which is then
|
||||||
|
// divided by 4 or 5 depending on other state. That's 7 times the
|
||||||
|
// rate provided to the CPU; given that the input is in half-cycles
|
||||||
|
// the proper multiplier is therefore 3.5.
|
||||||
|
case Personality::MDVDP: {
|
||||||
|
const int result = source * 7 + cycles_error_;
|
||||||
|
cycles_error_ = result & 1;
|
||||||
|
return result >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Provides the number of complete external cycles that lie between now and
|
Provides the number of complete external cycles that lie between now and
|
||||||
@c internal_cycles into the future. Any trailing fractional external cycle
|
@c internal_cycles into the future. Any trailing fractional external cycle
|
||||||
is discarded.
|
is discarded.
|
||||||
*/
|
*/
|
||||||
HalfCycles half_cycles_before_internal_cycles(int internal_cycles) const {
|
HalfCycles half_cycles_before_internal_cycles(int internal_cycles) const {
|
||||||
|
// Logic here correlates with multipliers as per @c to_internal.
|
||||||
|
switch(personality) {
|
||||||
|
default:
|
||||||
return HalfCycles(
|
return HalfCycles(
|
||||||
((internal_cycles << 2) + (2 - cycles_error_)) / 3
|
((internal_cycles << 2) + (2 - cycles_error_)) / 3
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case Personality::V9938:
|
||||||
|
case Personality::V9958:
|
||||||
|
return HalfCycles(internal_cycles / 3);
|
||||||
|
|
||||||
|
case Personality::MDVDP:
|
||||||
|
return HalfCycles(
|
||||||
|
((internal_cycles << 1) + (1 - cycles_error_)) / 7
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Converts a position in internal cycles to its corresponding position
|
Converts a position in internal cycles to its corresponding position
|
||||||
on the memory-access clock.
|
on the TMS memory-access clock, i.e. scales down to 171 clocks
|
||||||
|
per line
|
||||||
*/
|
*/
|
||||||
static constexpr int to_access_clock(int source) {
|
static constexpr int to_tms_access_clock(int source) {
|
||||||
|
switch(personality) {
|
||||||
|
default:
|
||||||
return source >> 1;
|
return source >> 1;
|
||||||
|
|
||||||
|
case Personality::V9938:
|
||||||
|
case Personality::V9958:
|
||||||
|
return source >> 3;
|
||||||
|
|
||||||
|
case Personality::MDVDP:
|
||||||
|
return source / 20;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of internal cycles in a single line.
|
/// The number of internal cycles in a single line.
|
||||||
constexpr static int CyclesPerLine = 342;
|
constexpr static int CyclesPerLine = cycles_per_line<personality>();
|
||||||
|
|
||||||
/// Indicates the number of access-window cycles in a single line.
|
|
||||||
constexpr static int AccessWindowCyclesPerLine = 171;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Holds current residue in conversion from the external to
|
// Holds current residue in conversion from the external to
|
||||||
|
Loading…
Reference in New Issue
Block a user