diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index 5eb230b4c..8bf87691d 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -481,12 +481,11 @@ void TMS9918::run_for(const HalfCycles cycles) { template void Base::output_border(int cycles, [[maybe_unused]] uint32_t cram_dot) { cycles = from_internal(cycles); - const uint32_t border_colour = - is_sega_vdp(personality) ? - master_system_.colour_ram[16 + background_colour_] : - palette[background_colour_]; + uint32_t border_colour; if constexpr (is_sega_vdp(personality)) { + border_colour = Storage::colour_ram_[16 + background_colour_]; + if(cram_dot) { uint32_t *const pixel_target = reinterpret_cast(crt_.begin_data(1)); if(pixel_target) { @@ -498,6 +497,8 @@ void Base::output_border(int cycles, [[maybe_unused]] uint32_t cram crt_.output_level(4); cycles -= 4; } + } else { + border_colour = palette[background_colour_]; } if(!cycles) { @@ -562,7 +563,7 @@ void Base::write_register(uint8_t value) { value &= 0x7f; } else if constexpr (is_sega_vdp(personality)) { if(value & 0x40) { - master_system_.cram_is_selected = true; + Storage::cram_is_selected_ = true; return; } value &= 0xf; @@ -574,12 +575,12 @@ void Base::write_register(uint8_t value) { switch(value) { case 0: if constexpr (is_sega_vdp(personality)) { - master_system_.vertical_scroll_lock = low_write_ & 0x80; - master_system_.horizontal_scroll_lock = low_write_ & 0x40; - master_system_.hide_left_column = low_write_ & 0x20; + Storage::master_system_.vertical_scroll_lock = low_write_ & 0x80; + Storage::master_system_.horizontal_scroll_lock = low_write_ & 0x40; + Storage::master_system_.hide_left_column = low_write_ & 0x20; enable_line_interrupts_ = low_write_ & 0x10; - master_system_.shift_sprites_8px_left = low_write_ & 0x08; - master_system_.mode4_enable = low_write_ & 0x04; + Storage::master_system_.shift_sprites_8px_left = low_write_ & 0x08; + Storage::master_system_.mode4_enable = low_write_ & 0x04; } mode2_enable_ = low_write_ & 0x02; break; @@ -599,7 +600,10 @@ void Base::write_register(uint8_t value) { case 2: pattern_name_address_ = size_t((low_write_ & 0xf) << 10) | 0x3ff; - master_system_.pattern_name_address = pattern_name_address_ | ((personality == TMS::SMSVDP) ? 0x000 : 0x400); + + if constexpr (is_sega_vdp(personality)) { + Storage::master_system_.pattern_name_address = pattern_name_address_ | ((personality == TMS::SMSVDP) ? 0x000 : 0x400); + } break; case 3: @@ -612,12 +616,16 @@ void Base::write_register(uint8_t value) { case 5: sprite_attribute_table_address_ = size_t((low_write_ & 0x7f) << 7) | 0x7f; - master_system_.sprite_attribute_table_address = sprite_attribute_table_address_ | ((personality == TMS::SMSVDP) ? 0x00 : 0x80); + if constexpr (is_sega_vdp(personality)) { + Storage::master_system_.sprite_attribute_table_address = sprite_attribute_table_address_ | ((personality == TMS::SMSVDP) ? 0x00 : 0x80); + } break; case 6: sprite_generator_table_address_ = size_t((low_write_ & 0x07) << 11) | 0x7ff; - master_system_.sprite_generator_table_address = sprite_generator_table_address_ | ((personality == TMS::SMSVDP) ? 0x0000 : 0x1800); + if constexpr (is_sega_vdp(personality)) { + Storage::master_system_.sprite_generator_table_address = sprite_generator_table_address_ | ((personality == TMS::SMSVDP) ? 0x0000 : 0x1800); + } break; case 7: @@ -627,7 +635,7 @@ void Base::write_register(uint8_t value) { case 8: if constexpr (is_sega_vdp(personality)) { - master_system_.horizontal_scroll = low_write_; + Storage::master_system_.horizontal_scroll = low_write_; } else { LOG("Unknown TMS write: " << int(low_write_) << " to " << int(value)); } @@ -635,7 +643,7 @@ void Base::write_register(uint8_t value) { case 9: if constexpr (is_sega_vdp(personality)) { - master_system_.vertical_scroll = low_write_; + Storage::master_system_.vertical_scroll = low_write_; } else { LOG("Unknown TMS write: " << int(low_write_) << " to " << int(value)); } @@ -667,7 +675,10 @@ void Base::write_register(uint8_t value) { queued_access_ = MemoryAccess::Read; cycles_until_access_ = Timing::VRAMAccessDelay; } - master_system_.cram_is_selected = false; + + if constexpr (is_sega_vdp(personality)) { + Storage::cram_is_selected_ = false; + } } } @@ -897,5 +908,5 @@ template class TI::TMS::TMS9918; //template class TI::TMS::TMS9918; template class TI::TMS::TMS9918; template class TI::TMS::TMS9918; -template class TI::TMS::TMS9918; +//template class TI::TMS::TMS9918; //template class TI::TMS::TMS9918; diff --git a/Components/9918/Implementation/9918Base.hpp b/Components/9918/Implementation/9918Base.hpp index 204c8cb03..6a219bc41 100644 --- a/Components/9918/Implementation/9918Base.hpp +++ b/Components/9918/Implementation/9918Base.hpp @@ -132,7 +132,39 @@ template struct Storage struct Storage> { - // TODO: relocate contents of master_system_ struct; + // The SMS VDP has a programmer-set colour palette, with a dedicated patch of RAM. But the RAM is only exactly + // fast enough for the pixel clock. So when the programmer writes to it, that causes a one-pixel glitch; there + // isn't the bandwidth for the read both write to occur simultaneously. The following buffer therefore keeps + // track of pending collisions, for visual reproduction. + struct CRAMDot { + LineBufferPointer location; + uint32_t value; + }; + std::vector upcoming_cram_dots_; + + // The Master System's additional colour RAM. + uint32_t colour_ram_[32]; + bool cram_is_selected_ = false; + + // Fields below affect only the Master System output mode. + struct { + // Programmer-set flags. + bool vertical_scroll_lock = false; + bool horizontal_scroll_lock = false; + bool hide_left_column = false; + bool shift_sprites_8px_left = false; + bool mode4_enable = false; + uint8_t horizontal_scroll = 0; + uint8_t vertical_scroll = 0; + + // Holds the vertical scroll position for this frame; this is latched + // once and cannot dynamically be changed until the next frame. + uint8_t latched_vertical_scroll = 0; + + size_t pattern_name_address; + size_t sprite_attribute_table_address; + size_t sprite_generator_table_address; + } master_system_; }; template struct Base: public Storage { @@ -269,40 +301,6 @@ template struct Base: public Storage { // with the beginning of writing the next, hence the two separate line buffers. LineBufferPointer read_pointer_, write_pointer_; - // The SMS VDP has a programmer-set colour palette, with a dedicated patch of RAM. But the RAM is only exactly - // fast enough for the pixel clock. So when the programmer writes to it, that causes a one-pixel glitch; there - // isn't the bandwidth for the read both write to occur simultaneously. The following buffer therefore keeps - // track of pending collisions, for visual reproduction. - struct CRAMDot { - LineBufferPointer location; - uint32_t value; - }; - std::vector upcoming_cram_dots_; - - // Extra information that affects the Master System output mode. - struct { - // Programmer-set flags. - bool vertical_scroll_lock = false; - bool horizontal_scroll_lock = false; - bool hide_left_column = false; - bool shift_sprites_8px_left = false; - bool mode4_enable = false; - uint8_t horizontal_scroll = 0; - uint8_t vertical_scroll = 0; - - // The Master System's additional colour RAM. - uint32_t colour_ram[32]; - bool cram_is_selected = false; - - // Holds the vertical scroll position for this frame; this is latched - // once and cannot dynamically be changed until the next frame. - uint8_t latched_vertical_scroll = 0; - - size_t pattern_name_address; - size_t sprite_attribute_table_address; - size_t sprite_generator_table_address; - } master_system_; - int masked_address(int address); void write_vram(uint8_t); void write_register(uint8_t); @@ -319,7 +317,7 @@ template struct Base: public Storage { } if constexpr (is_sega_vdp(personality)) { - if(master_system_.mode4_enable) { + if(Storage::master_system_.mode4_enable) { return ScreenMode::SMSMode4; } } @@ -356,12 +354,12 @@ template struct Base: public Storage { case MemoryAccess::Write: if constexpr (is_sega_vdp(personality)) { - if(master_system_.cram_is_selected) { + if(Storage::cram_is_selected_) { // Adjust the palette. In a Master System blue has a slightly different // scale; cf. https://www.retrorgb.com/sega-master-system-non-linear-blue-channel-findings.html constexpr uint8_t rg_scale[] = {0, 85, 170, 255}; constexpr uint8_t b_scale[] = {0, 104, 170, 255}; - master_system_.colour_ram[ram_pointer_ & 0x1f] = palette_pack( + Storage::colour_ram_[ram_pointer_ & 0x1f] = palette_pack( rg_scale[(read_ahead_buffer_ >> 0) & 3], rg_scale[(read_ahead_buffer_ >> 2) & 3], b_scale[(read_ahead_buffer_ >> 4) & 3] @@ -370,7 +368,7 @@ template struct Base: public Storage { // Schedule a CRAM dot; this is scheduled for wherever it should appear // on screen. So it's wherever the output stream would be now. Which // is output_lag cycles ago from the point of view of the input stream. - CRAMDot &dot = upcoming_cram_dots_.emplace_back(); + auto &dot = Storage::upcoming_cram_dots_.emplace_back(); dot.location.column = write_pointer_.column - output_lag; dot.location.row = write_pointer_.row; @@ -383,7 +381,7 @@ template struct Base: public Storage { dot.location.row += dot.location.column / 342; dot.location.column %= 342; - dot.value = master_system_.colour_ram[ram_pointer_ & 0x1f]; + dot.value = Storage::colour_ram_[ram_pointer_ & 0x1f]; break; } } diff --git a/Components/9918/Implementation/Draw.hpp b/Components/9918/Implementation/Draw.hpp index 4fe6da102..f997c3ace 100644 --- a/Components/9918/Implementation/Draw.hpp +++ b/Components/9918/Implementation/Draw.hpp @@ -133,6 +133,8 @@ void Base::draw_tms_text(int start, int end) { template void Base::draw_sms(int start, int end, uint32_t cram_dot) { + if constexpr (is_sega_vdp(personality)) { + LineBuffer &line_buffer = line_buffers_[read_pointer_.row]; int colour_buffer[256]; @@ -141,7 +143,7 @@ void Base::draw_sms(int start, int end, uint32_t cram_dot) { */ int tile_start = start, tile_end = end; int tile_offset = start; - if(read_pointer_.row >= 16 || !master_system_.horizontal_scroll_lock) { + if(read_pointer_.row >= 16 || !Storage::master_system_.horizontal_scroll_lock) { for(int c = start; c < (line_buffer.latched_horizontal_scroll & 7); ++c) { colour_buffer[c] = 16 + background_colour_; ++tile_offset; @@ -266,20 +268,22 @@ void Base::draw_sms(int start, int end, uint32_t cram_dot) { } // Map from the 32-colour buffer to real output pixels, applying the specific CRAM dot if any. - pixel_target_[start] = master_system_.colour_ram[colour_buffer[start] & 0x1f] | cram_dot; + pixel_target_[start] = Storage::colour_ram_[colour_buffer[start] & 0x1f] | cram_dot; for(int c = start+1; c < end; ++c) { - pixel_target_[c] = master_system_.colour_ram[colour_buffer[c] & 0x1f]; + pixel_target_[c] = Storage::colour_ram_[colour_buffer[c] & 0x1f]; } // If the VDP is set to hide the left column and this is the final call that'll come // this line, hide it. if(end == 256) { - if(master_system_.hide_left_column) { + if(Storage::master_system_.hide_left_column) { pixel_origin_[0] = pixel_origin_[1] = pixel_origin_[2] = pixel_origin_[3] = pixel_origin_[4] = pixel_origin_[5] = pixel_origin_[6] = pixel_origin_[7] = - master_system_.colour_ram[16 + background_colour_]; + Storage::colour_ram_[16 + background_colour_]; } } + + } } // MARK: - Yamaha diff --git a/Components/9918/Implementation/Fetch.hpp b/Components/9918/Implementation/Fetch.hpp index a2367de93..1e3697623 100644 --- a/Components/9918/Implementation/Fetch.hpp +++ b/Components/9918/Implementation/Fetch.hpp @@ -319,15 +319,17 @@ template void Base::fetch_tms_character(int start, in template template void Base::fetch_sms(int start, int end) { + if constexpr (is_sega_vdp(personality)) { + #define sprite_fetch(sprite) {\ line_buffer.active_sprites[sprite].x = \ ram_[\ - master_system_.sprite_attribute_table_address & size_t(0x3f80 | (line_buffer.active_sprites[sprite].index << 1))\ - ] - (master_system_.shift_sprites_8px_left ? 8 : 0); \ + Storage::master_system_.sprite_attribute_table_address & size_t(0x3f80 | (line_buffer.active_sprites[sprite].index << 1))\ + ] - (Storage::master_system_.shift_sprites_8px_left ? 8 : 0); \ const uint8_t name = ram_[\ - master_system_.sprite_attribute_table_address & size_t(0x3f81 | (line_buffer.active_sprites[sprite].index << 1))\ + Storage::master_system_.sprite_attribute_table_address & size_t(0x3f81 | (line_buffer.active_sprites[sprite].index << 1))\ ] & (sprites_16x16_ ? ~1 : ~0);\ - const size_t graphic_location = master_system_.sprite_generator_table_address & size_t(0x2000 | (name << 5) | (line_buffer.active_sprites[sprite].row << 2)); \ + const size_t graphic_location = Storage::master_system_.sprite_generator_table_address & size_t(0x2000 | (name << 5) | (line_buffer.active_sprites[sprite].row << 2)); \ line_buffer.active_sprites[sprite].image[0] = ram_[graphic_location]; \ line_buffer.active_sprites[sprite].image[1] = ram_[graphic_location+1]; \ line_buffer.active_sprites[sprite].image[2] = ram_[graphic_location+2]; \ @@ -346,8 +348,8 @@ template void Base::fetch_sms(int start, int end) { #define sprite_y_read(location, sprite) \ slot(location): \ - posit_sprite(sprite_selection_buffer, sprite, ram_[master_system_.sprite_attribute_table_address & ((sprite) | 0x3f00)], write_pointer_.row); \ - posit_sprite(sprite_selection_buffer, sprite+1, ram_[master_system_.sprite_attribute_table_address & ((sprite + 1) | 0x3f00)], write_pointer_.row); \ + posit_sprite(sprite_selection_buffer, sprite, ram_[Storage::master_system_.sprite_attribute_table_address & ((sprite) | 0x3f00)], write_pointer_.row); \ + posit_sprite(sprite_selection_buffer, sprite+1, ram_[Storage::master_system_.sprite_attribute_table_address & ((sprite + 1) | 0x3f00)], write_pointer_.row); \ #define fetch_tile_name(column, row_info) {\ const size_t scrolled_column = (column - horizontal_offset) & 0x1f;\ @@ -391,16 +393,16 @@ template void Base::fetch_sms(int start, int end) { // Determine the coarse horizontal scrolling offset; this isn't applied on the first two lines if the programmer has requested it. LineBuffer &line_buffer = line_buffers_[write_pointer_.row]; LineBuffer &sprite_selection_buffer = line_buffers_[(write_pointer_.row + 1) % mode_timing_.total_lines]; - const int horizontal_offset = (write_pointer_.row >= 16 || !master_system_.horizontal_scroll_lock) ? (line_buffer.latched_horizontal_scroll >> 3) : 0; + const int horizontal_offset = (write_pointer_.row >= 16 || !Storage::master_system_.horizontal_scroll_lock) ? (line_buffer.latched_horizontal_scroll >> 3) : 0; // Limit address bits in use if this is a SMS2 mode. const bool is_tall_mode = mode_timing_.pixel_lines != 192; - const size_t pattern_name_address = master_system_.pattern_name_address | (is_tall_mode ? 0x800 : 0); + const size_t pattern_name_address = Storage::master_system_.pattern_name_address | (is_tall_mode ? 0x800 : 0); const size_t pattern_name_offset = is_tall_mode ? 0x100 : 0; // Determine row info for the screen both (i) if vertical scrolling is applied; and (ii) if it isn't. // The programmer can opt out of applying vertical scrolling to the right-hand portion of the display. - const int scrolled_row = (write_pointer_.row + master_system_.latched_vertical_scroll) % (is_tall_mode ? 256 : 224); + const int scrolled_row = (write_pointer_.row + Storage::master_system_.latched_vertical_scroll) % (is_tall_mode ? 256 : 224); struct RowInfo { size_t pattern_address_base; size_t sub_row[2]; @@ -410,7 +412,7 @@ template void Base::fetch_sms(int start, int end) { {size_t((scrolled_row & 7) << 2), 28 ^ size_t((scrolled_row & 7) << 2)} }; RowInfo row_info; - if(master_system_.vertical_scroll_lock) { + if(Storage::master_system_.vertical_scroll_lock) { row_info.pattern_address_base = (pattern_name_address & size_t(((write_pointer_.row & ~7) << 3) | 0x3800)) - pattern_name_offset; row_info.sub_row[0] = size_t((write_pointer_.row & 7) << 2); row_info.sub_row[1] = 28 ^ size_t((write_pointer_.row & 7) << 2); @@ -463,6 +465,7 @@ template void Base::fetch_sms(int start, int end) { #undef sprite_y_read #undef sprite_fetch_block #undef sprite_fetch + } } // MARK: - Yamaha