diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index 9ce7ed2e7..03a7af1ef 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -167,10 +167,6 @@ void TMS9918::run_for(const HalfCycles cycles) { const int end_column = this->fetch_pointer_.column + fetch_cycles; LineBuffer &line_buffer = this->line_buffers_[this->fetch_pointer_.row]; - // Determine what this does to any enqueued VRAM access. - this->minimum_access_column_ = this->fetch_pointer_.column + this->cycles_until_access_; - this->cycles_until_access_ -= fetch_cycles; - // ... and to any pending Yamaha commands. if constexpr (is_yamaha_vdp(personality)) { if(Storage::command_) { @@ -279,11 +275,26 @@ void TMS9918::run_for(const HalfCycles cycles) { this->fetch_pointer_.column = end_column; fetch_cycles_pool -= fetch_cycles; + // Check for end of line. if(this->fetch_pointer_.column == Timing::CyclesPerLine) { this->fetch_pointer_.column = 0; this->fetch_pointer_.row = (this->fetch_pointer_.row + 1) % this->mode_timing_.total_lines; LineBuffer &next_line_buffer = this->line_buffers_[this->fetch_pointer_.row]; + // Progress towards any delayed events. + this->minimum_access_column_ = + std::max( + 0, + this->minimum_access_column_ - Timing::CyclesPerLine + ); + if constexpr (is_yamaha_vdp(personality)) { + Storage::minimum_command_column_ = + std::max( + 0, + Storage::minimum_command_column_ - Timing::CyclesPerLine + ); + } + // Establish the current screen output mode, which will be captured as a // line mode momentarily. this->screen_mode_ = this->current_screen_mode(); @@ -586,7 +597,7 @@ void Base::write_vram(uint8_t value) { // Enqueue the write to occur at the next available slot. read_ahead_buffer_ = value; queued_access_ = MemoryAccess::Write; - cycles_until_access_ = Timing::VRAMAccessDelay; + minimum_access_column_ = fetch_pointer_.column + Timing::VRAMAccessDelay; } template @@ -890,34 +901,9 @@ void Base::commit_register(int reg, uint8_t value) { #undef Begin // Seed timing information if a command was found. - if(Storage::command_) { - // TODO: eliminate SUPER HACK. - // This is currently here primarily to help me to hash out the - // proper interface, and also because I'm just not persuaded - // that minimum_access_column_ works properly. Why doesn't it - // ever end up out of bounds? + Storage::update_command_step(fetch_pointer_.column); - // TODO: undo temporary assumption of Graphics Mode 5 and of logical operation. - uint8_t packed_colour = Storage::command_context_.colour & 3; - packed_colour |= packed_colour << 2; - packed_colour |= packed_colour << 4; - - while(!Storage::command_->done()) { - const unsigned int address = - (Storage::command_->location.v[0] >> 2) + - (Storage::command_->location.v[1] << 7); - - const uint8_t mask = 0xc0 >> ((Storage::command_->location.v[0] & 3) << 1); - uint8_t v = ram_[address]; - v &= ~mask; - v |= packed_colour & mask; - ram_[address] = v; - - Storage::command_->advance(); - } - - Storage::command_ = nullptr; - } else { + if(!Storage::command_) { LOG("TODO: Yamaha command " << PADHEX(2) << +value); } break; @@ -951,7 +937,7 @@ void Base::write_register(uint8_t value) { // A read request is enqueued upon setting the address; conversely a write // won't be enqueued unless and until some actual data is supplied. queued_access_ = MemoryAccess::Read; - cycles_until_access_ = Timing::VRAMAccessDelay; + minimum_access_column_ = fetch_pointer_.column + Timing::VRAMAccessDelay; } if constexpr (is_sega_vdp(personality)) { @@ -1027,7 +1013,8 @@ uint8_t Base::read_register() { return (queued_access_ == MemoryAccess::None ? 0x80 : 0x00) | (is_vertical_blank() ? 0x40 : 0x00) | - (is_horizontal_blank() ? 0x20 : 0x00); + (is_horizontal_blank() ? 0x20 : 0x00) | + (Storage::command_ ? 0x01 : 0x00); break; } diff --git a/Components/9918/Implementation/9918Base.hpp b/Components/9918/Implementation/9918Base.hpp index d0f8a164b..55761f639 100644 --- a/Components/9918/Implementation/9918Base.hpp +++ b/Components/9918/Implementation/9918Base.hpp @@ -194,13 +194,15 @@ template struct Storagecycles; switch(command_->access) { case Command::AccessType::PlotPoint: next_command_step_ = CommandStep::ReadPixel; @@ -423,7 +425,6 @@ template struct Base: public Storage { size_t ram_pointer_ = 0; uint8_t read_ahead_buffer_ = 0; MemoryAccess queued_access_ = MemoryAccess::None; - int cycles_until_access_ = 0; int minimum_access_column_ = 0; // The main status register. @@ -574,14 +575,52 @@ template struct Base: public Storage { // has yet to pass. if(queued_access_ == MemoryAccess::None || access_column < minimum_access_column_) { if constexpr (is_yamaha_vdp(personality)) { -// if( -// Storage::next_command_step_ == Storage::CommandStep::None || -// access_column < Storage::minimum_access_column_ -// ) { -// return; -// } + using CommandStep = typename Storage::CommandStep; - // TODO: command-engine action. + if( + Storage::next_command_step_ == CommandStep::None || + access_column < Storage::minimum_command_column_ + ) { + return; + } + + // TODO: undo assumption of Graphics Mode 5, pervasively but starting here. + const unsigned int address = + (Storage::command_->location.v[0] >> 2) + + (Storage::command_->location.v[1] << 7); + + switch(Storage::next_command_step_) { + // Duplicative, but keeps the compiler happy. + case CommandStep::None: + break; + + case CommandStep::ReadPixel: + Storage::command_latch_ = ram_[address]; + + Storage::minimum_command_column_ = access_column + 24; + Storage::next_command_step_ = CommandStep::WritePixel; + break; + + case CommandStep::WritePixel: { + uint8_t packed_colour = Storage::command_context_.colour & 3; + packed_colour |= packed_colour << 2; + packed_colour |= packed_colour << 4; + + const uint8_t mask = 0xc0 >> ((Storage::command_->location.v[0] & 3) << 1); + Storage::command_latch_ &= ~mask; + Storage::command_latch_ |= packed_colour & mask; + ram_[address] = Storage::command_latch_; + + Storage::command_->advance(); + + if(Storage::command_->done()) { + Storage::command_ = nullptr; + Storage::next_command_step_ = CommandStep::None; + } else { + Storage::update_command_step(access_column); + } + } break; + } } return;