1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-17 13:29:02 +00:00
CLK/Components/9918/Implementation/ClockConverter.hpp

93 lines
2.5 KiB
C++
Raw Normal View History

//
// ClockConverter.hpp
// Clock Signal
//
// Created by Thomas Harte on 01/01/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef ClockConverter_hpp
#define ClockConverter_hpp
#include "../9918.hpp"
namespace TI {
namespace TMS {
2023-01-02 02:20:30 +00:00
/*!
This implementation of the TMS, etc mediates between three clocks:
1) the external clock, which is whatever the rest of the system(s)
it plugs into run at;
2) the internal clock, which is used to time and place syncs, borders,
pixel regions, etc; and
3) a memory acccess clock, which correlates to the number of windows
available for memory accesses.
E.g. for both a regular TMS9918 and the Sega Master System, the external
clock is 3.58Mhz, the internal clock is 5.37Mhz and the memory access
clock is 2.69Mhz.
2023-01-02 03:17:21 +00:00
Or, put another way, for both a TMS9918 and Master System:
* 228 external cycles;
* is 342 internal cycles;
* which exactly covers 228 NTSC colour clocks; and
* contains 171 memory access windows.
2023-01-02 02:20:30 +00:00
Both the Yamaha extensions and the Mega Drive VDP are a bit smarter about
paged mode memory accesses, obviating any advantage to treating (3) as a
separate clock.
*/
template <Personality personality> class ClockConverter {
public:
/*!
Given that another @c source external **half-cycles** has occurred,
indicates how many complete internal **cycles** have additionally elapsed
since the last call to @c to_internal.
*/
int to_internal(int source) {
// Default behaviour is top apply a multiplication by 3/4.
const int result = source * 3 + cycles_error_;
cycles_error_ = result & 3;
return result >> 2;
}
/*!
2023-01-02 02:20:30 +00:00
Provides the number of complete external cycles that lie between now and
@c internal_cycles into the future. Any trailing fractional external cycle
is discarded.
*/
HalfCycles half_cycles_before_internal_cycles(int internal_cycles) const {
return HalfCycles(
((internal_cycles << 2) + (2 - cycles_error_)) / 3
);
}
2023-01-02 02:20:30 +00:00
/*!
Converts a position in internal cycles to its corresponding position
2023-01-02 02:20:30 +00:00
on the memory-access clock.
*/
static constexpr int to_access_clock(int source) {
return source >> 1;
}
/// The number of internal cycles in a single line.
constexpr static int CyclesPerLine = 342;
/// Indicates the number of access-window cycles in a single line.
constexpr static int AccessWindowCyclesPerLine = 171;
private:
2023-01-02 02:20:30 +00:00
// Holds current residue in conversion from the external to
// internal clock.
int cycles_error_ = 0;
};
}
}
#endif /* ClockConverter_hpp */