1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-30 04:50:08 +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.
// ------------------------
#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 final_window = from_internal<personality, clock>(end_column); \
if(first_window == final_window) break; \
if(final_window != clock_rate<personality, clock>()) { \
function_true(first_window, final_window); \
function<true>(first_window, final_window); \
} 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) {
case LineMode::Text: fetch_simple(this->template fetch_tms_text, Clock::TMSMemoryWindow); break;
case LineMode::Character: fetch_simple(this->template fetch_tms_character, Clock::TMSMemoryWindow); break;
case LineMode::SMS: fetch_simple(this->template fetch_sms, Clock::TMSMemoryWindow); break;
case LineMode::Refresh: fetch_simple(this->template fetch_tms_refresh, Clock::TMSMemoryWindow); break;
case LineMode::Text: fetch(this->template fetch_tms_text, Clock::TMSMemoryWindow); break;
case LineMode::Character: fetch(this->template fetch_tms_character, Clock::TMSMemoryWindow); break;
case LineMode::SMS: fetch(this->template fetch_sms, Clock::TMSMemoryWindow); break;
case LineMode::Refresh: fetch(this->template fetch_tms_refresh, Clock::TMSMemoryWindow); break;
case LineMode::Yamaha:
#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;
case LineMode::Yamaha: fetch(this->template fetch_yamaha, Clock::Internal); break;
}
#undef fetch_simple
#undef fetch
// TODO: the above is too macro-heavy. Simplify.
// -------------------------------
// Check for interrupt conditions.
@ -316,8 +292,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
case ScreenMode::YamahaGraphics5:
case ScreenMode::YamahaGraphics6:
case ScreenMode::YamahaGraphics7:
// TODO: sprites?
next_line_buffer.line_mode = LineMode::YamahaNoSprites;
next_line_buffer.line_mode = LineMode::Yamaha;
break;
default:
// 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->is_vertical_blank())
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,
SMS,
Yamaha,
YamahaNoSprites,
};
enum class MemoryAccess {
@ -155,6 +154,7 @@ template <Personality personality, typename Enable = void> struct Storage {
};
template <> struct Storage<Personality::TMS9918A> {
void begin_line(ScreenMode, bool, bool) {}
};
// Yamaha-specific storage.
@ -169,6 +169,183 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
uint8_t palette_entry_ = 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.
@ -207,6 +384,8 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
size_t pattern_name_address_;
size_t sprite_attribute_table_address_;
size_t sprite_generator_table_address_;
void begin_line(ScreenMode, bool, bool) {}
};
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_character(int start, int end);
template<bool use_end> void fetch_yamaha_refresh(int start, int end);
template<bool use_end, bool fetch_sprites> void fetch_yamaha(int start, int end);
template<bool use_end> void fetch_yamaha(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
// 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<bool use_end> void Base<personality>::fetch_yamaha_refresh(int start, int end) {
(void)start;
(void)end;
}
template <Personality personality>
template<bool use_end, bool fetch_sprites> void Base<personality>::fetch_yamaha(int start, int end) {
template<bool use_end> void Base<personality>::fetch_yamaha([[maybe_unused]] int start, int end) {
if constexpr (is_yamaha_vdp(personality)) {
/*
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
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.
// 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
default: break;
}
/// 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;
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;
++Storage<personality>::next_event_;
}
}
}
// MARK: - Mega Drive