From 34722bae89731530d5795e62e9514cd345d8a472 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 3 Feb 2023 23:06:27 -0500 Subject: [PATCH 1/2] Start pivoting to a more natural expression of TMS patterns. --- Components/9918/Implementation/9918Base.hpp | 8 + Components/9918/Implementation/Fetch.hpp | 175 ++++++++++---------- 2 files changed, 93 insertions(+), 90 deletions(-) diff --git a/Components/9918/Implementation/9918Base.hpp b/Components/9918/Implementation/9918Base.hpp index 410262b82..480ed24c9 100644 --- a/Components/9918/Implementation/9918Base.hpp +++ b/Components/9918/Implementation/9918Base.hpp @@ -823,6 +823,14 @@ template struct Base: public Storage { queued_access_ = MemoryAccess::None; } + /// Helper for TMS dispatches; contains a switch statement with cases 0 to 170, each of the form: + /// + /// if constexpr (use_end && end == n) return; [[fallthrough]]; case n: fetcher.fetch(); + /// + /// i.e. it provides standard glue to enter a fetch sequence at any point, while the fetches themselves are templated on the cycle + /// at which they appear for neater expression. + template void dispatch(Fetcher &fetcher, int start, int end); + // Various fetchers. template void fetch_tms_refresh(LineBuffer &, int y, int start, int end); template void fetch_tms_text(LineBuffer &, int y, int start, int end); diff --git a/Components/9918/Implementation/Fetch.hpp b/Components/9918/Implementation/Fetch.hpp index b2a3d1f8b..f8053cc47 100644 --- a/Components/9918/Implementation/Fetch.hpp +++ b/Components/9918/Implementation/Fetch.hpp @@ -73,105 +73,100 @@ // MARK: - TMS9918 template -template void Base::fetch_tms_refresh(LineBuffer &, int, int start, int end) { -#define refresh(location) \ - slot(location): \ - external_slot(location+1); - -#define refreshes_2(location) \ - refresh(location); \ - refresh(location+2); - -#define refreshes_4(location) \ - refreshes_2(location); \ - refreshes_2(location+4); - -#define refreshes_8(location) \ - refreshes_4(location); \ - refreshes_4(location+8); +template void Base::dispatch(Fetcher &fetcher, int start, int end) { +#define index(n) \ + if(use_end && end == n) return; \ + [[fallthrough]]; \ + case n: fetcher.template fetch(); switch(start) { default: assert(false); - - /* 44 external slots */ - external_slots_32(0) - external_slots_8(32) - external_slots_4(40) - - /* 64 refresh/external slot pairs (= 128 windows) */ - refreshes_8(44); - refreshes_8(60); - refreshes_8(76); - refreshes_8(92); - refreshes_8(108); - refreshes_8(124); - refreshes_8(140); - refreshes_8(156); - - return; + index(0); index(1); index(2); index(3); index(4); index(5); index(6); index(7); index(8); index(9); + index(10); index(11); index(12); index(13); index(14); index(15); index(16); index(17); index(18); index(19); + index(20); index(21); index(22); index(23); index(24); index(25); index(26); index(27); index(28); index(29); + index(30); index(31); index(32); index(33); index(34); index(35); index(36); index(37); index(38); index(39); + index(40); index(41); index(42); index(43); index(44); index(45); index(46); index(47); index(48); index(49); + index(50); index(51); index(52); index(53); index(54); index(55); index(56); index(57); index(58); index(59); + index(60); index(61); index(62); index(63); index(64); index(65); index(66); index(67); index(68); index(69); + index(70); index(71); index(72); index(73); index(74); index(75); index(76); index(77); index(78); index(79); + index(80); index(81); index(82); index(83); index(84); index(85); index(86); index(87); index(88); index(89); + index(90); index(91); index(92); index(93); index(94); index(95); index(96); index(97); index(98); index(99); + index(100); index(101); index(102); index(103); index(104); index(105); index(106); index(107); index(108); index(109); + index(110); index(111); index(112); index(113); index(114); index(115); index(116); index(117); index(118); index(119); + index(120); index(121); index(122); index(123); index(124); index(125); index(126); index(127); index(128); index(129); + index(130); index(131); index(132); index(133); index(134); index(135); index(136); index(137); index(138); index(139); + index(140); index(141); index(142); index(143); index(144); index(145); index(146); index(147); index(148); index(149); + index(150); index(151); index(152); index(153); index(154); index(155); index(156); index(157); index(158); index(159); + index(160); index(161); index(162); index(163); index(164); index(165); index(166); index(167); index(168); index(169); + index(170); } -#undef refreshes_8 -#undef refreshes_4 -#undef refreshes_2 -#undef refresh +#undef index +} + +namespace { + +template +struct RefreshFetcher { + RefreshFetcher(Base *base) : base(base) {} + + template void fetch() { + // Do 44 external slots in a block, then treat every other slot as external. + if(cycle < 44 || (cycle&1)) { + base->do_external_slot(to_internal(cycle)); + } + } + + Base *const base; +}; + +template +struct TextFetcher { + using AddressT = typename Base::AddressT; + + TextFetcher(Base *base, LineBuffer &buffer, int y) : + base(base), + line_buffer(buffer), + row_base(base->pattern_name_address_ & (0x3c00 | size_t(y >> 3) * 40)), + row_offset(base->pattern_generator_table_address_ & (0x3800 | (y & 7))) {} + + template void fetch() { + // The first 47 and the final 4 slots are external. + if constexpr (cycle < 47 || cycle >= 167) { + base->do_external_slot(to_internal(cycle)); + return; + } + + // For the 120 slots in between follow a three-step pattern of: + if constexpr (cycle >= 47) { + constexpr int offset = cycle - 47; + constexpr auto column = AddressT(offset / 3); + switch(offset % 3) { + case 0: line_buffer.names[column].offset = base->ram_[row_base + column]; break; // (1) fetch tile name. + case 1: base->do_external_slot(to_internal(cycle)); break; // (2) external slot. + case 2: line_buffer.patterns[column][0] = base->ram_[row_offset + size_t(line_buffer.names[column].offset << 3)]; break; // (3) fetch tile pattern. + } + } + } + + Base *const base; + LineBuffer &line_buffer; + const AddressT row_base; + const AddressT row_offset; +}; + +} + +template +template void Base::fetch_tms_refresh(LineBuffer &, int, int start, int end) { + RefreshFetcher refresher(this); + dispatch(refresher, start, end); } template template void Base::fetch_tms_text(LineBuffer &line_buffer, int y, int start, int end) { -#define fetch_tile_name(location, column) slot(location): line_buffer.names[column].offset = ram_[row_base + column]; -#define fetch_tile_pattern(location, column) slot(location): line_buffer.patterns[column][0] = ram_[row_offset + size_t(line_buffer.names[column].offset << 3)]; - -#define fetch_column(location, column) \ - fetch_tile_name(location, column); \ - external_slot(location+1); \ - fetch_tile_pattern(location+2, column); - -#define fetch_columns_2(location, column) \ - fetch_column(location, column); \ - fetch_column(location+3, column+1); - -#define fetch_columns_4(location, column) \ - fetch_columns_2(location, column); \ - fetch_columns_2(location+6, column+2); - -#define fetch_columns_8(location, column) \ - fetch_columns_4(location, column); \ - fetch_columns_4(location+12, column+4); - - const size_t row_base = pattern_name_address_ & (0x3c00 | size_t(y >> 3) * 40); - const size_t row_offset = pattern_generator_table_address_ & (0x3800 | (y & 7)); - - switch(start) { - default: assert(false); - - /* 47 external slots (= 47 windows) */ - external_slots_32(0) - external_slots_8(32) - external_slots_4(40) - external_slots_2(44) - external_slot(46) - - /* 40 column fetches (= 120 windows) */ - fetch_columns_8(47, 0); - fetch_columns_8(71, 8); - fetch_columns_8(95, 16); - fetch_columns_8(119, 24); - fetch_columns_8(143, 32); - - /* 5 more external slots */ - external_slots_4(167); - external_slot(171); - - return; - } - -#undef fetch_columns_8 -#undef fetch_columns_4 -#undef fetch_columns_2 -#undef fetch_column -#undef fetch_tile_pattern -#undef fetch_tile_name + TextFetcher fetcher(this, line_buffer, y); + dispatch(fetcher, start, end); } template From 46d009f27b439187e74ed845ad6ff25e399eecf1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 4 Feb 2023 10:31:41 -0500 Subject: [PATCH 2/2] Add logical fill. --- Components/9918/Implementation/9918.cpp | 4 ++-- Components/9918/Implementation/YamahaCommands.hpp | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index 574fc600a..80c67c833 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -885,12 +885,12 @@ void Base::commit_register(int reg, uint8_t value) { case 0b0110: break; // TODO: srch. [search horizontally for a colour] case 0b0111: Begin(Line); break; // LINE - case 0b1000: break; // TODO: lmmv. [logical move, VDP to VRAM] + case 0b1000: Begin(LogicalFill); break; // LMMV [logical move, VDP to VRAM, i.e. solid-colour fill] case 0b1001: break; // TODO: lmmm. [logical move, VRAM to VRAM] case 0b1010: break; // TODO: lmcm. [logical move, VRAM to CPU] case 0b1011: Begin(LogicalMoveFromCPU); break; // LMMC [logical move, CPU to VRAM] - case 0b1100: Begin(HighSpeedFill); break; // HMMV [high-speed move, VDP to VRAM] + case 0b1100: Begin(HighSpeedFill); break; // HMMV [high-speed move, VDP to VRAM, i.e. single-byte fill] case 0b1101: break; // TODO: hmmm. [high-speed move, VRAM to VRAM] case 0b1110: break; // TODO: ymmm. [high-speed move, y only, VRAM to VRAM] case 0b1111: break; // TODO: hmmc. [high-speed move, CPU to VRAM] diff --git a/Components/9918/Implementation/YamahaCommands.hpp b/Components/9918/Implementation/YamahaCommands.hpp index 85754d179..f0273b5d4 100644 --- a/Components/9918/Implementation/YamahaCommands.hpp +++ b/Components/9918/Implementation/YamahaCommands.hpp @@ -296,6 +296,20 @@ struct HighSpeedFill: public Rectangle { } }; +struct LogicalFill: public Rectangle { + LogicalFill(CommandContext &context) : Rectangle(context) { + cycles = 64; + access = AccessType::PlotPoint; + } + + void advance(int pixels_per_byte) final { + cycles = 72; + if(!advance_pixel(pixels_per_byte)) { + cycles += 64; + } + } +}; + } } }