1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-30 07:55:01 +00:00

Start attempting to use table-based Yamaha fetch.

This commit is contained in:
Thomas Harte 2023-01-22 22:00:28 -05:00
parent c6dd7d4726
commit 91047e5b3a
3 changed files with 221 additions and 174 deletions

View File

@ -200,52 +200,28 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
// ------------------------ // ------------------------
// Perform memory accesses. // Perform memory accesses.
// ------------------------ // ------------------------
#define fetch(function_true, function_false, clock) { \ #define fetch(function, clock) { \
const int first_window = from_internal<personality, clock>(this->fetch_pointer_.column);\ const int first_window = from_internal<personality, clock>(this->fetch_pointer_.column);\
const int final_window = from_internal<personality, clock>(end_column); \ const int final_window = from_internal<personality, clock>(end_column); \
if(first_window == final_window) break; \ if(first_window == final_window) break; \
if(final_window != clock_rate<personality, clock>()) { \ if(final_window != clock_rate<personality, clock>()) { \
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); \
} \ } \
} }
#define fetch_simple(function, clock) fetch(function<true>, function<false>, clock)
switch(line_buffer.line_mode) { switch(line_buffer.line_mode) {
case LineMode::Text: fetch_simple(this->template fetch_tms_text, Clock::TMSMemoryWindow); break; case LineMode::Text: fetch(this->template fetch_tms_text, Clock::TMSMemoryWindow); break;
case LineMode::Character: fetch_simple(this->template fetch_tms_character, Clock::TMSMemoryWindow); break; case LineMode::Character: fetch(this->template fetch_tms_character, Clock::TMSMemoryWindow); break;
case LineMode::SMS: fetch_simple(this->template fetch_sms, Clock::TMSMemoryWindow); break; case LineMode::SMS: fetch(this->template fetch_sms, Clock::TMSMemoryWindow); break;
case LineMode::Refresh: fetch_simple(this->template fetch_tms_refresh, Clock::TMSMemoryWindow); break; case LineMode::Refresh: fetch(this->template fetch_tms_refresh, Clock::TMSMemoryWindow); break;
case LineMode::Yamaha: case LineMode::Yamaha: fetch(this->template fetch_yamaha, Clock::Internal); break;
#define true_func this->template fetch_yamaha<true, true>
#define false_func this->template fetch_yamaha<false, true>
fetch(
true_func,
false_func,
Clock::Internal);
#undef true_func
#undef false_func
break;
#define true_func this->template fetch_yamaha<true, false>
#define false_func this->template fetch_yamaha<false, false>
case LineMode::YamahaNoSprites:
fetch(
true_func,
false_func,
Clock::Internal);
#undef true_func
#undef false_func
break;
} }
#undef fetch_simple
#undef fetch #undef fetch
// TODO: the above is too macro-heavy. Simplify.
// ------------------------------- // -------------------------------
// Check for interrupt conditions. // Check for interrupt conditions.
@ -316,8 +292,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
case ScreenMode::YamahaGraphics5: case ScreenMode::YamahaGraphics5:
case ScreenMode::YamahaGraphics6: case ScreenMode::YamahaGraphics6:
case ScreenMode::YamahaGraphics7: case ScreenMode::YamahaGraphics7:
// TODO: sprites? next_line_buffer.line_mode = LineMode::Yamaha;
next_line_buffer.line_mode = LineMode::YamahaNoSprites;
break; break;
default: default:
// This covers both MultiColour and Graphics modes. // This covers both MultiColour and Graphics modes.
@ -329,6 +304,9 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
(this->screen_mode_ == ScreenMode::Blank) || (this->screen_mode_ == ScreenMode::Blank) ||
this->is_vertical_blank()) this->is_vertical_blank())
next_line_buffer.line_mode = LineMode::Refresh; next_line_buffer.line_mode = LineMode::Refresh;
// TODO: an actual sprites-enabled flag.
Storage<personality>::begin_line(this->screen_mode_, next_line_buffer.line_mode == LineMode::Refresh, false);
} }
} }

View File

@ -59,7 +59,6 @@ enum class LineMode {
Refresh, Refresh,
SMS, SMS,
Yamaha, Yamaha,
YamahaNoSprites,
}; };
enum class MemoryAccess { enum class MemoryAccess {
@ -155,6 +154,7 @@ template <Personality personality, typename Enable = void> struct Storage {
}; };
template <> struct Storage<Personality::TMS9918A> { template <> struct Storage<Personality::TMS9918A> {
void begin_line(ScreenMode, bool, bool) {}
}; };
// Yamaha-specific storage. // Yamaha-specific storage.
@ -169,6 +169,183 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
uint8_t palette_entry_ = 0; uint8_t palette_entry_ = 0;
uint8_t mode_ = 0; uint8_t mode_ = 0;
/// Describes an _observable_ memory access event. i.e. anything that it is safe
/// (and convenient) to treat as atomic in between external slots.
struct Event {
/// Offset of the _beginning_ of the event. Not completely arbitrarily: this is when
/// external data must be ready by in order to take part in those slots.
int offset = 1368;
enum class Type {
External,
DataBlock,
} type = Type::External;
constexpr Event(int offset, Type type) noexcept :
offset(grauw_to_internal(offset)),
type(type) {}
constexpr Event(int offset) noexcept :
offset(grauw_to_internal(offset)) {}
constexpr Event() noexcept {}
};
const Event *next_event_ = nullptr;
void begin_line([[maybe_unused]] ScreenMode mode, bool is_refresh, [[maybe_unused]] bool sprites_enabled) {
// TODO: remove this check. It's temporary, while the Yamaha is still using the TMS fetchers.
if(mode < ScreenMode::YamahaText80) {
return;
}
assert(next_event_ == nullptr || next_event_->offset == 1368);
if(is_refresh) {
next_event_ = refresh_events;
return;
}
// TODO: obey sprites_enabled flag, at least.
next_event_ = no_sprites_events;
}
Storage() noexcept {
// Perform sanity checks on the event lists.
#ifndef NDEBUG
const Event *lists[] = { no_sprites_events, refresh_events, nullptr };
const Event **list = lists;
while(*list) {
const Event *cursor = *list;
++list;
while(cursor[1].offset != 1368) {
assert(cursor[1].offset > cursor[0].offset);
++cursor;
}
}
#endif
}
private:
// This emulator treats position 0 as being immediately after the standard pixel area.
// i.e. offset 1282 on Grauw's http://map.grauw.nl/articles/vdp-vram-timing/vdp-timing.png
constexpr static int grauw_to_internal(int offset) {
return (offset + 1368 - 1282) % 1368;
}
static constexpr Event refresh_events[] = {
Event(1284), Event(1292), Event(1300), Event(1308), Event(1316), Event(1324),
Event(1334), Event(1344), Event(1352), Event(1360), Event(0), Event(8),
Event(16), Event(24), Event(32), Event(40), Event(48), Event(56),
Event(64), Event(72), Event(80), Event(88), Event(96), Event(104),
Event(112), Event(120),
Event(164), Event(172), Event(180), Event(188), Event(196), Event(204),
Event(212), Event(220), Event(228), Event(236), Event(244), Event(252),
Event(260), Event(268), Event(276), /* Refresh. */ Event(292), Event(300),
Event(308), Event(316), Event(324), Event(332), Event(340), Event(348),
Event(356), Event(364), Event(372), Event(380), Event(388), Event(396),
Event(404), /* Refresh. */ Event(420), Event(428), Event(436), Event(444),
Event(452), Event(460), Event(468), Event(476), Event(484), Event(492),
Event(500), Event(508), Event(516), Event(524), Event(532), /* Refresh. */
Event(548), Event(556), Event(564), Event(570), Event(580), Event(588),
Event(596), Event(604), Event(612), Event(620), Event(628), Event(636),
Event(644), Event(652), Event(660), /* Refresh. */ Event(676), Event(684),
Event(692), Event(700), Event(708), Event(716), Event(724), Event(732),
Event(740), Event(748), Event(756), Event(764), Event(772), Event(780),
Event(788), /* Refresh. */ Event(804), Event(812), Event(820), Event(828),
Event(836), Event(844), Event(852), Event(860), Event(868), Event(876),
Event(884), Event(892), Event(900), Event(908), Event(916), /* Refresh. */
Event(932), Event(940), Event(948), Event(956), Event(964), Event(972),
Event(980), Event(988), Event(996), Event(1004), Event(1012), Event(1020),
Event(1028), Event(1036), Event(1044), /* Refresh. */ Event(1060), Event(1068),
Event(1076), Event(1084), Event(1092), Event(1100), Event(1108), Event(1116),
Event(1124), Event(1132), Event(1140), Event(1148), Event(1156), Event(1164),
Event(1172), /* Refresh. */ Event(1188), Event(1196), Event(1204), Event(1212),
Event(1220), Event(1228),
Event(1268), Event(1276),
Event()
};
static constexpr Event no_sprites_events[] = {
Event(1282, Event::Type::External),
Event(1290, Event::Type::External),
Event(1298, Event::Type::External),
Event(1306, Event::Type::External),
Event(1314, Event::Type::External),
Event(1322, Event::Type::External),
Event(1332, Event::Type::External),
Event(1342, Event::Type::External),
Event(1350, Event::Type::External),
Event(1358, Event::Type::External),
Event(1366, Event::Type::External),
Event(6, Event::Type::External),
Event(14, Event::Type::External),
Event(22, Event::Type::External),
Event(30, Event::Type::External),
Event(38, Event::Type::External),
Event(46, Event::Type::External),
Event(54, Event::Type::External),
Event(62, Event::Type::External),
Event(70, Event::Type::External),
Event(78, Event::Type::External),
Event(86, Event::Type::External),
Event(94, Event::Type::External),
Event(102, Event::Type::External),
Event(110, Event::Type::External),
Event(118, Event::Type::External),
Event(162, Event::Type::External),
Event(170, Event::Type::External),
Event(182, Event::Type::External),
Event(188, Event::Type::External),
// Omitted: dummy data block. Is not observable.
Event(214, Event::Type::External),
Event(220, Event::Type::External),
Event(226, Event::Type::DataBlock), Event(246, Event::Type::External), Event(252, Event::Type::External),
Event(258, Event::Type::DataBlock), Event(278, Event::Type::External), // Omitted: refresh.
Event(290, Event::Type::DataBlock), Event(310, Event::Type::External), Event(316, Event::Type::External),
Event(322, Event::Type::DataBlock), Event(342, Event::Type::External), Event(348, Event::Type::External),
Event(354, Event::Type::DataBlock), Event(374, Event::Type::External), Event(380, Event::Type::External),
Event(386, Event::Type::DataBlock), Event(406, Event::Type::External), // Omitted: refresh.
Event(418, Event::Type::DataBlock), Event(438, Event::Type::External), Event(444, Event::Type::External),
Event(450, Event::Type::DataBlock), Event(470, Event::Type::External), Event(476, Event::Type::External),
Event(482, Event::Type::DataBlock), Event(502, Event::Type::External), Event(508, Event::Type::External),
Event(514, Event::Type::DataBlock), Event(534, Event::Type::External), // Omitted: refresh.
Event(546, Event::Type::DataBlock), Event(566, Event::Type::External), Event(572, Event::Type::External),
Event(578, Event::Type::DataBlock), Event(598, Event::Type::External), Event(604, Event::Type::External),
Event(610, Event::Type::DataBlock), Event(630, Event::Type::External), Event(636, Event::Type::External),
Event(642, Event::Type::DataBlock), Event(662, Event::Type::External), // Omitted: refresh.
Event(674, Event::Type::DataBlock), Event(694, Event::Type::External), Event(700, Event::Type::External),
Event(706, Event::Type::DataBlock), Event(726, Event::Type::External), Event(732, Event::Type::External),
Event(738, Event::Type::DataBlock), Event(758, Event::Type::External), Event(764, Event::Type::External),
Event(770, Event::Type::DataBlock), Event(790, Event::Type::External), // Omitted: refresh.
Event(802, Event::Type::DataBlock), Event(822, Event::Type::External), Event(828, Event::Type::External),
Event(834, Event::Type::DataBlock), Event(854, Event::Type::External), Event(860, Event::Type::External),
Event(866, Event::Type::DataBlock), Event(886, Event::Type::External), Event(892, Event::Type::External),
Event(898, Event::Type::DataBlock), Event(918, Event::Type::External), // Omitted: refresh.
Event(930, Event::Type::DataBlock), Event(950, Event::Type::External), Event(956, Event::Type::External),
Event(962, Event::Type::DataBlock), Event(982, Event::Type::External), Event(988, Event::Type::External),
Event(994, Event::Type::DataBlock), Event(1014, Event::Type::External), Event(1020, Event::Type::External),
Event(1026, Event::Type::DataBlock), Event(1046, Event::Type::External), // Omitted: refresh.
Event(1058, Event::Type::DataBlock), Event(1078, Event::Type::External), Event(1084, Event::Type::External),
Event(1090, Event::Type::DataBlock), Event(1110, Event::Type::External), Event(1116, Event::Type::External),
Event(1122, Event::Type::DataBlock), Event(1142, Event::Type::External), Event(1148, Event::Type::External),
Event(1154, Event::Type::DataBlock), Event(1174, Event::Type::External), // Omitted: refresh.
Event(1186, Event::Type::DataBlock), Event(1206, Event::Type::External), Event(1212, Event::Type::External),
Event(1218, Event::Type::DataBlock),
Event(1266, Event::Type::External),
Event(1274, Event::Type::External),
Event()
};
}; };
// Master System-specific storage. // Master System-specific storage.
@ -207,6 +384,8 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
size_t pattern_name_address_; size_t pattern_name_address_;
size_t sprite_attribute_table_address_; size_t sprite_attribute_table_address_;
size_t sprite_generator_table_address_; size_t sprite_generator_table_address_;
void begin_line(ScreenMode, bool, bool) {}
}; };
template <Personality personality> struct Base: public Storage<personality> { template <Personality personality> struct Base: public Storage<personality> {
@ -475,8 +654,7 @@ template <Personality personality> struct Base: public Storage<personality> {
template<bool use_end> void fetch_tms_text(int start, int end); template<bool use_end> void fetch_tms_text(int start, int end);
template<bool use_end> void fetch_tms_character(int start, int end); template<bool use_end> void fetch_tms_character(int start, int end);
template<bool use_end> void fetch_yamaha_refresh(int start, int end); template<bool use_end> void fetch_yamaha(int start, int end);
template<bool use_end, bool fetch_sprites> void fetch_yamaha(int start, int end);
template<bool use_end> void fetch_sms(int start, int end); template<bool use_end> void fetch_sms(int start, int end);

View File

@ -470,26 +470,9 @@ template<bool use_end> void Base<personality>::fetch_sms(int start, int end) {
// MARK: - Yamaha // MARK: - Yamaha
// TODO.
namespace {
// This emulator treats position 0 as being immediately after the standard pixel area.
// i.e. offset 1282 on Grauw's http://map.grauw.nl/articles/vdp-vram-timing/vdp-timing.png
constexpr int grauw_to_internal(int offset) {
return (offset + 1368 - 1282) % 1368;
}
}
template <Personality personality> template <Personality personality>
template<bool use_end> void Base<personality>::fetch_yamaha_refresh(int start, int end) { template<bool use_end> void Base<personality>::fetch_yamaha([[maybe_unused]] int start, int end) {
(void)start; if constexpr (is_yamaha_vdp(personality)) {
(void)end;
}
template <Personality personality>
template<bool use_end, bool fetch_sprites> void Base<personality>::fetch_yamaha(int start, int end) {
/* /*
Per http://map.grauw.nl/articles/vdp-vram-timing/vdp-timing.html : Per http://map.grauw.nl/articles/vdp-vram-timing/vdp-timing.html :
@ -508,110 +491,18 @@ template<bool use_end, bool fetch_sprites> void Base<personality>::fetch_yamaha(
these dummy reads don't matter, it only matters that at those moments no other VRAM accesses these dummy reads don't matter, it only matters that at those moments no other VRAM accesses
can occur. can occur.
*/ */
while(Storage<personality>::next_event_->offset < end) {
switch(Storage<personality>::next_event_->type) {
case Storage<personality>::Event::Type::External:
do_external_slot(Storage<personality>::next_event_->offset);
break;
// This emulator treats position 0 as being immediately after the standard pixel area. default: break;
// i.e. offset 1282 on Grauw's http://map.grauw.nl/articles/vdp-vram-timing/vdp-timing.png }
//
// That being the case...
//
// Data blocks are located at:
//
// 194
/// Describes an _observable_ memory access event. i.e. anything that it is safe ++Storage<personality>::next_event_;
/// (and convenient) to treat as atomic in between external slots. }
struct Event { }
/// Offset of the _beginning_ of the event. Not completely arbitrarily: this is when
/// external data must be ready by in order to take part in those slots.
int offset;
enum class Type {
External,
DataBlock,
} type;
constexpr Event(int offset, Type type) noexcept :
offset(grauw_to_internal(offset)),
type(type) {}
};
constexpr Event no_sprites_events[] = {
Event(1282, Event::Type::External),
Event(1290, Event::Type::External),
Event(1298, Event::Type::External),
Event(1306, Event::Type::External),
Event(1314, Event::Type::External),
Event(1322, Event::Type::External),
Event(1332, Event::Type::External),
Event(1342, Event::Type::External),
Event(1350, Event::Type::External),
Event(1358, Event::Type::External),
Event(1366, Event::Type::External),
Event(6, Event::Type::External),
Event(14, Event::Type::External),
Event(22, Event::Type::External),
Event(30, Event::Type::External),
Event(38, Event::Type::External),
Event(46, Event::Type::External),
Event(54, Event::Type::External),
Event(62, Event::Type::External),
Event(70, Event::Type::External),
Event(78, Event::Type::External),
Event(86, Event::Type::External),
Event(94, Event::Type::External),
Event(102, Event::Type::External),
Event(110, Event::Type::External),
Event(118, Event::Type::External),
Event(162, Event::Type::External),
Event(170, Event::Type::External),
Event(182, Event::Type::External),
Event(188, Event::Type::External),
// Omitted: dummy data block. Is not observable.
Event(214, Event::Type::External),
Event(220, Event::Type::External),
Event(226, Event::Type::DataBlock), Event(246, Event::Type::External), Event(252, Event::Type::External),
Event(258, Event::Type::DataBlock), Event(278, Event::Type::External), // Omitted: refresh.
Event(290, Event::Type::DataBlock), Event(310, Event::Type::External), Event(316, Event::Type::External),
Event(322, Event::Type::DataBlock), Event(342, Event::Type::External), Event(348, Event::Type::External),
Event(354, Event::Type::DataBlock), Event(374, Event::Type::External), Event(380, Event::Type::External),
Event(386, Event::Type::DataBlock), Event(406, Event::Type::External), // Omitted: refresh.
Event(418, Event::Type::DataBlock), Event(438, Event::Type::External), Event(444, Event::Type::External),
Event(450, Event::Type::DataBlock), Event(470, Event::Type::External), Event(476, Event::Type::External),
Event(482, Event::Type::DataBlock), Event(502, Event::Type::External), Event(508, Event::Type::External),
Event(514, Event::Type::DataBlock), Event(534, Event::Type::External), // Omitted: refresh.
Event(546, Event::Type::DataBlock), Event(566, Event::Type::External), Event(572, Event::Type::External),
Event(578, Event::Type::DataBlock), Event(598, Event::Type::External), Event(604, Event::Type::External),
Event(610, Event::Type::DataBlock), Event(630, Event::Type::External), Event(636, Event::Type::External),
Event(642, Event::Type::DataBlock), Event(662, Event::Type::External), // Omitted: refresh.
Event(674, Event::Type::DataBlock), Event(694, Event::Type::External), Event(700, Event::Type::External),
Event(706, Event::Type::DataBlock), Event(726, Event::Type::External), Event(732, Event::Type::External),
Event(738, Event::Type::DataBlock), Event(758, Event::Type::External), Event(764, Event::Type::External),
Event(770, Event::Type::DataBlock), Event(790, Event::Type::External), // Omitted: refresh.
Event(802, Event::Type::DataBlock), Event(822, Event::Type::External), Event(828, Event::Type::External),
Event(834, Event::Type::DataBlock), Event(854, Event::Type::External), Event(860, Event::Type::External),
Event(866, Event::Type::DataBlock), Event(886, Event::Type::External), Event(892, Event::Type::External),
Event(898, Event::Type::DataBlock), Event(918, Event::Type::External), // Omitted: refresh.
Event(930, Event::Type::DataBlock), Event(950, Event::Type::External), Event(956, Event::Type::External),
Event(962, Event::Type::DataBlock), Event(982, Event::Type::External), Event(988, Event::Type::External),
Event(994, Event::Type::DataBlock), Event(1014, Event::Type::External), Event(1020, Event::Type::External),
Event(1026, Event::Type::DataBlock), Event(1046, Event::Type::External), // Omitted: refresh.
Event(1058, Event::Type::DataBlock), Event(1078, Event::Type::External), Event(1084, Event::Type::External),
Event(1090, Event::Type::DataBlock), Event(1110, Event::Type::External), Event(1116, Event::Type::External),
Event(1122, Event::Type::DataBlock), Event(1142, Event::Type::External), Event(1148, Event::Type::External),
Event(1154, Event::Type::DataBlock), Event(1174, Event::Type::External), // Omitted: refresh.
Event(1186, Event::Type::DataBlock), Event(1206, Event::Type::External), Event(1212, Event::Type::External),
Event(1218, Event::Type::DataBlock),
Event(1266, Event::Type::External),
Event(1274, Event::Type::External),
};
(void)start;
(void)end;
} }
// MARK: - Mega Drive // MARK: - Mega Drive