diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index 2ab4ac9d7..74eab1d85 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -184,6 +184,9 @@ void TMS9918::run_for(const HalfCycles cycles) { const int end_column = write_pointer_.column + write_cycles; LineBuffer &line_buffer = line_buffers_[write_pointer_.row]; + // Determine what this does to any enqueued VRAM access. + minimum_access_column_ = write_pointer_.column + cycles_until_access_; + cycles_until_access_ -= write_cycles; // --------------------------------------- @@ -487,6 +490,7 @@ void TMS9918::set_register(int address, uint8_t value) { // Enqueue the write to occur at the next available slot. read_ahead_buffer_ = value; queued_access_ = MemoryAccess::Write; + cycles_until_access_ = vram_access_delay(); return; } @@ -509,11 +513,11 @@ void TMS9918::set_register(int address, uint8_t value) { write_phase_ = false; if(value & 0x80) { if(is_sega_vdp(personality_)) { - if(value & 0x40) { - master_system_.cram_is_selected = true; - return; - } - value &= 0xf; + if(value & 0x40) { + master_system_.cram_is_selected = true; + return; + } + value &= 0xf; } else { value &= 0x7; } @@ -602,6 +606,7 @@ void TMS9918::set_register(int address, 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_ = vram_access_delay(); } master_system_.cram_is_selected = false; } diff --git a/Components/9918/Implementation/9918Base.hpp b/Components/9918/Implementation/9918Base.hpp index 7d2708919..e326ba5bc 100644 --- a/Components/9918/Implementation/9918Base.hpp +++ b/Components/9918/Implementation/9918Base.hpp @@ -36,7 +36,7 @@ enum class TVStandard { NTSC }; -#define is_sega_vdp(x) x >= SMSVDP +#define is_sega_vdp(x) ((x) >= SMSVDP) class Base { public: @@ -91,6 +91,14 @@ class Base { enum class MemoryAccess { Read, Write, None } queued_access_ = MemoryAccess::None; + int cycles_until_access_ = 0; + int minimum_access_column_ = 0; + int vram_access_delay() { + // The Sega VDP seems to allow slightly quicker access; + // Sega types generally claim 26 Z80 cycles are sufficient. + // The received wisdom in MSX land is that it's 27. + return is_sega_vdp(personality_) ? 7 : 8; + } // Holds the main status register. uint8_t status_ = 0; @@ -328,7 +336,11 @@ class Base { } void do_external_slot(int access_column) { - // TODO: is queued access ready yet? + // Don't do anything if the required time for the access to become executable + // has yet to pass. + if(access_column < minimum_access_column_) { + return; + } switch(queued_access_) { default: return;