| e8555c89 Andy McCall |
1 |
#include "VDPAccessSlots.hh" |
| 2 |
|
| 3 |
#include "one_of.hh" |
| 4 |
|
| 5 |
#include <algorithm> |
| 6 |
#include <array> |
| 7 |
#include <bitset> |
| 8 |
#include <cassert> |
| 9 |
#include <utility> |
| 10 |
|
| 11 |
namespace openmsx::VDPAccessSlots { |
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
static constexpr std::array<int16_t, 154 + 17> slotsScreenOff = { |
| 20 |
0, 8, 16, 24, 32, 40, 48, 56, 64, 72, |
| 21 |
80, 88, 96, 104, 112, 120, 164, 172, 180, 188, |
| 22 |
196, 204, 212, 220, 228, 236, 244, 252, 260, 268, |
| 23 |
276, 292, 300, 308, 316, 324, 332, 340, 348, 356, |
| 24 |
364, 372, 380, 388, 396, 404, 420, 428, 436, 444, |
| 25 |
452, 460, 468, 476, 484, 492, 500, 508, 516, 524, |
| 26 |
532, 548, 556, 564, 572, 580, 588, 596, 604, 612, |
| 27 |
620, 628, 636, 644, 652, 660, 676, 684, 692, 700, |
| 28 |
708, 716, 724, 732, 740, 748, 756, 764, 772, 780, |
| 29 |
788, 804, 812, 820, 828, 836, 844, 852, 860, 868, |
| 30 |
876, 884, 892, 900, 908, 916, 932, 940, 948, 956, |
| 31 |
964, 972, 980, 988, 996, 1004, 1012, 1020, 1028, 1036, |
| 32 |
1044, 1060, 1068, 1076, 1084, 1092, 1100, 1108, 1116, 1124, |
| 33 |
1132, 1140, 1148, 1156, 1164, 1172, 1188, 1196, 1204, 1212, |
| 34 |
1220, 1228, 1268, 1276, 1284, 1292, 1300, 1308, 1316, 1324, |
| 35 |
1334, 1344, 1352, 1360, |
| 36 |
1368+ 0, 1368+ 8, 1368+16, 1368+ 24, 1368+ 32, |
| 37 |
1368+ 40, 1368+ 48, 1368+56, 1368+ 64, 1368+ 72, |
| 38 |
1368+ 80, 1368+ 88, 1368+96, 1368+104, 1368+112, |
| 39 |
1368+120, 1368+164 |
| 40 |
}; |
| 41 |
|
| 42 |
|
| 43 |
static constexpr std::array<int16_t, 88 + 16> slotsSpritesOff = { |
| 44 |
6, 14, 22, 30, 38, 46, 54, 62, 70, 78, |
| 45 |
86, 94, 102, 110, 118, 162, 170, 182, 188, 214, |
| 46 |
220, 246, 252, 278, 310, 316, 342, 348, 374, 380, |
| 47 |
406, 438, 444, 470, 476, 502, 508, 534, 566, 572, |
| 48 |
598, 604, 630, 636, 662, 694, 700, 726, 732, 758, |
| 49 |
764, 790, 822, 828, 854, 860, 886, 892, 918, 950, |
| 50 |
956, 982, 988, 1014, 1020, 1046, 1078, 1084, 1110, 1116, |
| 51 |
1142, 1148, 1174, 1206, 1212, 1266, 1274, 1282, 1290, 1298, |
| 52 |
1306, 1314, 1322, 1332, 1342, 1350, 1358, 1366, |
| 53 |
1368+ 6, 1368+14, 1368+ 22, 1368+ 30, 1368+ 38, |
| 54 |
1368+ 46, 1368+54, 1368+ 62, 1368+ 70, 1368+ 78, |
| 55 |
1368+ 86, 1368+94, 1368+102, 1368+110, 1368+118, |
| 56 |
1368+162, |
| 57 |
}; |
| 58 |
|
| 59 |
|
| 60 |
static constexpr std::array<int16_t, 31 + 3> slotsSpritesOn = { |
| 61 |
28, 92, 162, 170, 188, 220, 252, 316, 348, 380, |
| 62 |
444, 476, 508, 572, 604, 636, 700, 732, 764, 828, |
| 63 |
860, 892, 956, 988, 1020, 1084, 1116, 1148, 1212, 1264, |
| 64 |
1330, |
| 65 |
1368+28, 1368+92, 1368+162, |
| 66 |
}; |
| 67 |
|
| 68 |
|
| 69 |
static constexpr std::array<int16_t, 31 + 3> slotsChar = { |
| 70 |
32, 96, 166, 174, 188, 220, 252, 316, 348, 380, |
| 71 |
444, 476, 508, 572, 604, 636, 700, 732, 764, 828, |
| 72 |
860, 892, 956, 988, 1020, 1084, 1116, 1148, 1212, 1268, |
| 73 |
1334, |
| 74 |
1368+32, 1368+96, 1368+166, |
| 75 |
}; |
| 76 |
|
| 77 |
|
| 78 |
static constexpr std::array<int16_t, 47 + 10> slotsText = { |
| 79 |
2, 10, 18, 26, 34, 42, 50, 58, 66, 166, |
| 80 |
174, 182, 190, 198, 206, 214, 222, 312, 408, 504, |
| 81 |
600, 696, 792, 888, 984, 1080, 1176, 1206, 1214, 1222, |
| 82 |
1230, 1238, 1246, 1254, 1262, 1270, 1278, 1286, 1294, 1302, |
| 83 |
1310, 1318, 1326, 1336, 1346, 1354, 1362, |
| 84 |
1368+ 2, 1368+10, 1368+18, 1368+26, 1368+ 34, |
| 85 |
1368+42, 1368+50, 1368+58, 1368+66, 1368+166, |
| 86 |
}; |
| 87 |
|
| 88 |
|
| 89 |
|
| 90 |
|
| 91 |
static constexpr std::array<int16_t, 107 + 18> slotsMsx1ScreenOff = { |
| 92 |
4, 12, 20, 28, 36, 44, 52, 60, 68, 76, |
| 93 |
84, 92, 100, 108, 116, 124, 132, 140, 148, 156, |
| 94 |
164, 172, 180, 188, 196, 204, 220, 236, 252, 268, |
| 95 |
284, 300, 316, 332, 348, 364, 380, 396, 412, 428, |
| 96 |
444, 460, 476, 492, 508, 524, 540, 556, 572, 588, |
| 97 |
604, 620, 636, 652, 668, 684, 700, 716, 732, 748, |
| 98 |
764, 780, 796, 812, 828, 844, 860, 876, 892, 908, |
| 99 |
924, 940, 956, 972, 988, 1004, 1020, 1036, 1052, 1068, |
| 100 |
1084, 1100, 1116, 1132, 1148, 1164, 1180, 1196, 1212, 1228, |
| 101 |
1236, 1244, 1252, 1260, 1268, 1276, 1284, 1292, 1300, 1308, |
| 102 |
1316, 1324, 1332, 1340, 1348, 1356, 1364, |
| 103 |
1368+ 4, 1368+ 12, 1368+ 20, 1368+ 28, 1368+ 36, |
| 104 |
1368+ 44, 1368+ 52, 1368+ 60, 1368+ 68, 1368+ 76, |
| 105 |
1368+ 84, 1368+ 92, 1368+100, 1368+108, 1368+116, |
| 106 |
1368+124, 1368+132, 1368+140, |
| 107 |
}; |
| 108 |
|
| 109 |
|
| 110 |
static constexpr std::array<int16_t, 19 + 8> slotsMsx1Gfx12 = { |
| 111 |
4, 12, 20, 28, 116, 124, 132, 140, 220, 348, |
| 112 |
476, 604, 732, 860, 988, 1116, 1236, 1244, 1364, |
| 113 |
1368+ 4, 1368+ 12, 1368+ 20, 1368+ 28, 1368+116, |
| 114 |
1368+124, 1368+132, 1368+140, |
| 115 |
}; |
| 116 |
|
| 117 |
|
| 118 |
static constexpr std::array<int16_t, 51 + 8> slotsMsx1Gfx3 = { |
| 119 |
4, 12, 20, 28, 116, 124, 132, 140, 220, 228, |
| 120 |
260, 292, 324, 348, 356, 388, 420, 452, 476, 484, |
| 121 |
516, 548, 580, 604, 612, 644, 676, 708, 732, 740, |
| 122 |
772, 804, 836, 860, 868, 900, 932, 964, 988, 996, |
| 123 |
1028, 1060, 1092, 1116, 1124, 1156, 1188, 1220, 1236, 1244, |
| 124 |
1364, |
| 125 |
1368+ 4, 1368+ 12, 1368+ 20, 1368+ 28, 1368+116, |
| 126 |
1368+124, 1368+132, 1368+140, |
| 127 |
}; |
| 128 |
|
| 129 |
|
| 130 |
|
| 131 |
static constexpr std::array<int16_t, 91 + 18> slotsMsx1Text = { |
| 132 |
4, 12, 20, 28, 36, 44, 52, 60, 68, 76, |
| 133 |
84, 92, 100, 108, 116, 124, 132, 140, 148, 156, |
| 134 |
164, 172, 180, 188, 196, 204, 212, 220, 228, 244, |
| 135 |
268, 292, 316, 340, 364, 388, 412, 436, 460, 484, |
| 136 |
508, 532, 556, 580, 604, 628, 652, 676, 700, 724, |
| 137 |
748, 772, 796, 820, 844, 868, 892, 916, 940, 964, |
| 138 |
988, 1012, 1036, 1060, 1084, 1108, 1132, 1156, 1180, 1196, |
| 139 |
1204, 1212, 1220, 1228, 1236, 1244, 1252, 1260, 1268, 1276, |
| 140 |
1284, 1292, 1300, 1308, 1316, 1324, 1332, 1340, 1348, 1356, |
| 141 |
1364, |
| 142 |
1368+ 4, 1368+ 12, 1368+ 20, 1368+ 28, 1368+ 36, |
| 143 |
1368+ 44, 1368+ 52, 1368+ 60, 1368+ 68, 1368+ 76, |
| 144 |
1368+ 84, 1368+ 92, 1368+100, 1368+108, 1368+116, |
| 145 |
1368+124, 1368+132, 1368+140, |
| 146 |
}; |
| 147 |
|
| 148 |
|
| 149 |
|
| 150 |
|
| 151 |
|
| 152 |
static constexpr size_t MAX_SLOTS = 192; |
| 153 |
|
| 154 |
|
| 155 |
|
| 156 |
|
| 157 |
|
| 158 |
|
| 159 |
|
| 160 |
|
| 161 |
|
| 162 |
|
| 163 |
|
| 164 |
|
| 165 |
|
| 166 |
|
| 167 |
|
| 168 |
|
| 169 |
|
| 170 |
|
| 171 |
|
| 172 |
|
| 173 |
|
| 174 |
|
| 175 |
|
| 176 |
|
| 177 |
|
| 178 |
|
| 179 |
|
| 180 |
|
| 181 |
|
| 182 |
|
| 183 |
|
| 184 |
|
| 185 |
|
| 186 |
|
| 187 |
|
| 188 |
|
| 189 |
|
| 190 |
|
| 191 |
|
| 192 |
|
| 193 |
|
| 194 |
|
| 195 |
|
| 196 |
|
| 197 |
struct Timing { |
| 198 |
struct Pad { |
| 199 |
int16_t at = 0; |
| 200 |
int16_t cycles = 0; |
| 201 |
}; |
| 202 |
std::array<Pad, 3> pad = {}; |
| 203 |
int extra = 0; |
| 204 |
int16_t lateLead = 0; |
| 205 |
std::array<int16_t, 2> plainSlots = {-1, -1}; |
| 206 |
}; |
| 207 |
|
| 208 |
struct AccessTable |
| 209 |
{ |
| 210 |
std::array<uint8_t, NUM_DELTAS * TICKS> values = {}; |
| 211 |
Timing timing; |
| 212 |
}; |
| 213 |
|
| 214 |
|
| 215 |
[[nodiscard]] static constexpr bool isLate(const Timing& timing, int tick) |
| 216 |
{ |
| 217 |
return (timing.lateLead != 0) && !contains(timing.plainSlots, tick); |
| 218 |
} |
| 219 |
|
| 220 |
struct CycleTable : AccessTable |
| 221 |
{ |
| 222 |
constexpr CycleTable(bool msx1, std::span<const int16_t> slots, |
| 223 |
Timing timing_ = {}) |
| 224 |
{ |
| 225 |
timing = timing_; |
| 226 |
assert(std::ranges::is_sorted(slots)); |
| 227 |
|
| 228 |
|
| 229 |
constexpr std::array<int, NUM_DELTAS> delta = { |
| 230 |
0, 1, 16, 28, 16, 24, 32, 36, 46, 60, 72, 84, 88, 36+68, 84+36, |
| 231 |
60+68, 72+58, 63, 64, 76, 100, 112 |
| 232 |
}; |
| 233 |
|
| 234 |
|
| 235 |
|
| 236 |
|
| 237 |
|
| 238 |
std::array<int16_t, 2 * TICKS> mem = {}; |
| 239 |
for (int t = 1; t < 2 * TICKS; ++t) { |
| 240 |
int m = mem[t - 1] + 1; |
| 241 |
for (const auto& p : timing.pad) { |
| 242 |
if (p.at && ((t == p.at) || (t == p.at + TICKS))) m -= p.cycles; |
| 243 |
} |
| 244 |
mem[t] = narrow_cast<int16_t>(m); |
| 245 |
} |
| 246 |
|
| 247 |
|
| 248 |
|
| 249 |
|
| 250 |
|
| 251 |
std::bitset<TICKS> tight; |
| 252 |
static_vector<int16_t, 25> tightSlots; |
| 253 |
std::array<uint8_t, MAX_SLOTS> cpuLead = {}; |
| 254 |
for (auto i : xrange(slots.size())) { |
| 255 |
auto s = slots[i]; |
| 256 |
if ((i > 0) && (s >= 6) && ((slots[i - 1] + 6) == s) && (s < TICKS)) { |
| 257 |
tight[s] = true; |
| 258 |
tightSlots.push_back(s); |
| 259 |
} |
| 260 |
cpuLead[i] = isLate(timing, s % TICKS) |
| 261 |
? narrow_cast<uint8_t>(timing.lateLead) : 0; |
| 262 |
} |
| 263 |
assert(tightSlots.size() == one_of(0u, 25u)); |
| 264 |
|
| 265 |
size_t out = 0; |
| 266 |
for (auto idx : xrange(NUM_DELTAS)) { |
| 267 |
bool cmd = (FIRST_CMD_DELTA <= idx) && (idx < LAST_CMD_DELTA); |
| 268 |
bool cpu = (FIRST_CPU_DELTA <= idx) && (idx < LAST_CPU_DELTA); |
| 269 |
bool skipTight = cpu && (idx != CPU_ANY_DELTA); |
| 270 |
int step = delta[idx] + (cmd ? timing.extra : 0); |
| 271 |
int p = 0; |
| 272 |
for (auto i : xrange(TICKS)) { |
| 273 |
|
| 274 |
|
| 275 |
auto ok = [&](int q) { |
| 276 |
if (cpu) { |
| 277 |
|
| 278 |
|
| 279 |
|
| 280 |
|
| 281 |
if (skipTight && tight[slots[q] % TICKS]) return false; |
| 282 |
return (mem[slots[q]] - mem[i]) >= (step + cpuLead[q]); |
| 283 |
} |
| 284 |
return cmd ? ((mem[slots[q]] - mem[i]) >= step) |
| 285 |
: ((slots[q] - i) >= step); |
| 286 |
}; |
| 287 |
while (!ok(p)) ++p; |
| 288 |
while ((p > 0) && ok(p - 1)) --p; |
| 289 |
unsigned t = slots[p] - i; |
| 290 |
if (msx1) { |
| 291 |
if (step <= 40) assert(t < 256); |
| 292 |
} else { |
| 293 |
assert(t < 256); |
| 294 |
} |
| 295 |
values[out++] = narrow_cast<uint8_t>(t); |
| 296 |
} |
| 297 |
} |
| 298 |
|
| 299 |
|
| 300 |
|
| 301 |
|
| 302 |
|
| 303 |
|
| 304 |
for (auto idx : xrange(FIRST_CMD_DELTA, LAST_CMD_DELTA)) { |
| 305 |
for (auto ts : tightSlots) { |
| 306 |
size_t b = (size_t(idx) * TICKS) + ts; |
| 307 |
values[b] = narrow_cast<uint8_t>(values[b + 1] + 1); |
| 308 |
} |
| 309 |
} |
| 310 |
} |
| 311 |
}; |
| 312 |
|
| 313 |
static constexpr CycleTable tabSpritesOn {false, slotsSpritesOn, Timing{.pad = {{{1330, 2}, {1337, 1}, {1348, 1}}}, .extra = 1, .lateLead = 2, .plainSlots = {162, 170}}}; |
| 314 |
static constexpr CycleTable tabSpritesOff {false, slotsSpritesOff, Timing{.pad = {{{1332, 2}, {1342, 2}}}, .extra = 0}}; |
| 315 |
static constexpr CycleTable tabChar {false, slotsChar, Timing{.lateLead = 2, .plainSlots = {166, 174}}}; |
| 316 |
static constexpr CycleTable tabText {false, slotsText}; |
| 317 |
static constexpr CycleTable tabScreenOff {false, slotsScreenOff, Timing{.pad = {{{1334, 2}, {1344, 2}}}, .extra = 0}}; |
| 318 |
static constexpr CycleTable tabMsx1Gfx12 {true, slotsMsx1Gfx12}; |
| 319 |
static constexpr CycleTable tabMsx1Gfx3 {true, slotsMsx1Gfx3}; |
| 320 |
static constexpr CycleTable tabMsx1Text {true, slotsMsx1Text}; |
| 321 |
static constexpr CycleTable tabMsx1ScreenOff{true, slotsMsx1ScreenOff}; |
| 322 |
static constexpr AccessTable tabBroken{}; |
| 323 |
|
| 324 |
|
| 325 |
[[nodiscard]] static inline const AccessTable& getTable(const VDP& vdp) |
| 326 |
{ |
| 327 |
if (vdp.getBrokenCmdTiming()) return tabBroken; |
| 328 |
|
| 329 |
|
| 330 |
bool enabled = vdp.isSlotTableDisplayEnabled(); |
| 331 |
bool sprites = vdp.spritesEnabledRegister(); |
| 332 |
auto mode = vdp.getDisplayMode(); |
| 333 |
bool bitmap = mode.isBitmapMode(); |
| 334 |
bool text = mode.isTextMode(); |
| 335 |
bool gfx3 = mode.getBase() == DisplayMode::GRAPHIC3; |
| 336 |
|
| 337 |
if (vdp.isMSX1VDP()) { |
| 338 |
if (!enabled) return tabMsx1ScreenOff; |
| 339 |
return text ? tabMsx1Text |
| 340 |
: (gfx3 ? tabMsx1Gfx3 |
| 341 |
: tabMsx1Gfx12); |
| 342 |
|
| 343 |
} else { |
| 344 |
if (bitmap) { |
| 345 |
return !enabled ? tabScreenOff |
| 346 |
: sprites ? tabSpritesOn |
| 347 |
: tabSpritesOff; |
| 348 |
} else { |
| 349 |
|
| 350 |
|
| 351 |
return text ? tabText |
| 352 |
: tabChar; |
| 353 |
} |
| 354 |
} |
| 355 |
} |
| 356 |
|
| 357 |
EmuTime getAccessSlot( |
| 358 |
EmuTime frame_, EmuTime time, Delta delta, |
| 359 |
const VDP& vdp) |
| 360 |
{ |
| 361 |
VDP::VDPClock frame(frame_); |
| 362 |
unsigned ticks = frame.getTicksTill(time) % TICKS; |
| 363 |
const auto& tab = getTable(vdp); |
| 364 |
return time + VDP::VDPClock::duration(tab.values[std::to_underlying(delta) + ticks]); |
| 365 |
} |
| 366 |
|
| 367 |
Calculator getCalculator( |
| 368 |
EmuTime frame, EmuTime time, EmuTime limit, |
| 369 |
const VDP& vdp) |
| 370 |
{ |
| 371 |
return {frame, time, limit, getTable(vdp).values}; |
| 372 |
} |
| 373 |
|
| 374 |
int paddingCycles(int tick, int n, const VDP& vdp) |
| 375 |
{ |
| 376 |
assert(0 <= tick); assert(tick < TICKS); |
| 377 |
assert(0 < n); assert(n <= MAX_PADDING_SPAN); |
| 378 |
|
| 379 |
int result = 0; |
| 380 |
for (const auto& p : getTable(vdp).timing.pad) { |
| 381 |
if (!p.at) continue; |
| 382 |
int d = p.at - tick; |
| 383 |
if (d <= 0) d += TICKS; |
| 384 |
if (d <= n) result += p.cycles; |
| 385 |
} |
| 386 |
return result; |
| 387 |
} |
| 388 |
|
| 389 |
bool isLateCpuSlot(int slotTick, const VDP& vdp) |
| 390 |
{ |
| 391 |
return isLate(getTable(vdp).timing, slotTick); |
| 392 |
} |
| 393 |
|
| 394 |
} |