From e7f4babf4161d6c373443674d4d6344d90b27672 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Sep 2018 22:59:16 -0400 Subject: [PATCH] Starts taking steps towards SMS/GG and V9938/9958 support. Specifically: routine namespace stuff, plus the intention to move to a table-based operation+cost version of timing. Reordering works fine for the TMS, and probably would also for the SMS/GG, but it'd be problematic with the command engine of the V9938/9958 and maintaining a consistent set of code is easier. --- Components/9918/9918.cpp | 30 +++-- Components/9918/9918.hpp | 10 +- Components/9918/Implementation/9918Base.hpp | 122 +++++++++++++++++++- Machines/ColecoVision/ColecoVision.cpp | 4 +- Machines/MSX/MSX.cpp | 4 +- 5 files changed, 150 insertions(+), 20 deletions(-) diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index c64bbac72..d5b1dbdb2 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -11,7 +11,7 @@ #include #include -using namespace TI; +using namespace TI::TMS; namespace { @@ -83,12 +83,28 @@ enum ScreenMode { } -TMS9918Base::TMS9918Base() : +Base::Base(Personality p) : // 342 internal cycles are 228/227.5ths of a line, so 341.25 cycles should be a whole // line. Therefore multiply everything by four, but set line length to 1365 rather than 342*4 = 1368. - crt_(new Outputs::CRT::CRT(1365, 4, Outputs::CRT::DisplayType::NTSC60, 4)) {} + crt_(new Outputs::CRT::CRT(1365, 4, Outputs::CRT::DisplayType::NTSC60, 4)) { -TMS9918::TMS9918(Personality p) { + switch(p) { + case TI::TMS::TMS9918A: + case TI::TMS::SMSVDP: + case TI::TMS::GGVDP: + ram_.resize(16 * 1024); + break; + case TI::TMS::V9938: + ram_.resize(128 * 1024); + break; + case TI::TMS::V9958: + ram_.resize(192 * 1024); + break; + } +} + +TMS9918::TMS9918(Personality p): + Base(p) { // Unimaginatively, this class just passes RGB through to the shader. Investigation is needed // into whether there's a more natural form. crt_->set_rgb_sampling_function( @@ -111,7 +127,7 @@ Outputs::CRT::CRT *TMS9918::get_crt() { return crt_.get(); } -void TMS9918Base::test_sprite(int sprite_number, int screen_row) { +void Base::test_sprite(int sprite_number, int screen_row) { if(!(status_ & StatusFifthSprite)) { status_ = static_cast((status_ & ~31) | sprite_number); } @@ -140,7 +156,7 @@ void TMS9918Base::test_sprite(int sprite_number, int screen_row) { sprite_sets_[active_sprite_set_].active_sprite_slot++; } -void TMS9918Base::get_sprite_contents(int field, int cycles_left, int screen_row) { +void Base::get_sprite_contents(int field, int cycles_left, int screen_row) { int sprite_id = field / 6; field %= 6; @@ -617,7 +633,7 @@ void TMS9918::run_for(const HalfCycles cycles) { } } -void TMS9918Base::output_border(int cycles) { +void Base::output_border(int cycles) { pixel_target_ = reinterpret_cast(crt_->allocate_write_area(1)); if(pixel_target_) *pixel_target_ = palette[background_colour_]; crt_->output_level(static_cast(cycles) * 4); diff --git a/Components/9918/9918.hpp b/Components/9918/9918.hpp index 4f2eff794..cf02cd074 100644 --- a/Components/9918/9918.hpp +++ b/Components/9918/9918.hpp @@ -17,6 +17,7 @@ #include namespace TI { +namespace TMS { /*! Provides emulation of the TMS9918a, TMS9928 and TMS9929. Likely in the future to be the @@ -29,12 +30,8 @@ namespace TI { These chips have only one non-on-demand interaction with the outside world: an interrupt line. See get_time_until_interrupt and get_interrupt_line for asynchronous operation options. */ -class TMS9918: public TMS9918Base { +class TMS9918: public Base { public: - enum Personality { - TMS9918A, // includes the 9928 and 9929; set TV standard and output device as desired. - }; - /*! Constructs an instance of the drive controller that behaves according to personality @c p. @param p The type of controller to emulate. @@ -81,6 +78,7 @@ class TMS9918: public TMS9918Base { bool get_interrupt_line(); }; -}; +} +} #endif /* TMS9918_hpp */ diff --git a/Components/9918/Implementation/9918Base.hpp b/Components/9918/Implementation/9918Base.hpp index 862c7807c..220587cb6 100644 --- a/Components/9918/Implementation/9918Base.hpp +++ b/Components/9918/Implementation/9918Base.hpp @@ -16,14 +16,23 @@ #include namespace TI { +namespace TMS { -class TMS9918Base { +enum Personality { + TMS9918A, // includes the 9928 and 9929; set TV standard and output device as desired. + V9938, + V9958, + SMSVDP, + GGVDP, +}; + +class Base { protected: - TMS9918Base(); + Base(Personality p); std::unique_ptr crt_; - uint8_t ram_[16384]; + std::vector ram_; uint16_t ram_pointer_ = 0; uint8_t read_ahead_buffer_ = 0; @@ -94,8 +103,115 @@ class TMS9918Base { inline void test_sprite(int sprite_number, int screen_row); inline void get_sprite_contents(int start, int cycles, int screen_row); + + // Contains tables describing the memory access patterns and, implicitly, + // the timing of video generation. + enum Operation { + HSyncOn, + HSyncOff, + ColourBurstOn, + ColourBurstOff, + + /// A memory access slot that is available for an external read or write. + External, + + /// A refresh cycle; neither used for video fetching nor available for external use. + Refresh, + + /*! + Column N Name Table Read + [= 1 slot] + */ + NameTableRead, + + /*! + Column N Pattern Table Read + [= 1 slot] + */ + PatternTableRead, + + /*! + Y0, X0, N0, C0, Pattern 0 (1), Pattern 0 (2), + Y1, X1, N1, C1, Pattern 1 (1), Pattern 1 (2), + Y2, X2 + [= 14 slots] + */ + TMSSpriteFetch1, + + /*! + N2, C2, Pattern 2 (1), Pattern 2 (2), + Y3, X3, N3, C3, Pattern 3 (1), Pattern 3 (2), + [= 10 slots] + */ + TMSSpriteFetch2, + + /*! + Sprite N fetch, Sprite N+1 fetch [...] + */ + TMSSpriteYFetch, + + /*! + Colour N, Pattern N, + Name N+1, + Sprite N, + + Colour N+1, Pattern N+1, + Name N+2, + Sprite N+1, + + Colour N+2, Pattern N+2, + Name N+3, + Sprite N+2, + + Colour N+3, Pattern N+3, + Name N+4, + [= 15 slots] + */ + TMSBackgroundRenderBlock, + + /*! + Pattern N, + Name N+1 + */ + TMSPatternNameFetch, + + /*! + Sprite N X/Name Read + Sprite N+1 X/Name Read + Sprite N Tile read (1st word) + Sprite N Tile read (2nd word) + Sprite N+1 Tile Read (1st word) + Sprite N+1 Tile Read (2nd word) + [= 6 slots] + */ + SMSSpriteRenderBlock, + + /*! + Column N Tile Read (1st word) + Column N Tile Read (2nd word) + Column N+1 Name Table Read + Sprite (16+N*1.5) Y Read (Reads Y of 2 sprites) + Column N+1 Tile Read (1st word) + Column N+1 Tile Read (2nd word) + Column N+2 Name Table Read + Sprite (16+N*1.5+2) Y Read (Reads Y of 2 sprites) + Column N+2 Tile Read (1st word) + Column N+2 Tile Read (2nd word) + Column N+3 Name Table Read + Sprite (16+N*1.5+4) Y Read (Reads Y of 2 sprites) + Column N+3 Tile Read (1st word) + Column N+3 Tile Read (2nd word) + [= 14 slots] + */ + SMSBackgroundRenderBlock, + }; + struct Period { + Operation operation; + int duration; + }; }; +} } #endif /* TMS9918Base_hpp */ diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index fbcd3ebb6..8452dd124 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -161,7 +161,7 @@ class ConcreteMachine: } void setup_output(float aspect_ratio) override { - vdp_.reset(new TI::TMS9918(TI::TMS9918::TMS9918A)); + vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A)); get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite); } @@ -358,7 +358,7 @@ class ConcreteMachine: } CPU::Z80::Processor z80_; - std::unique_ptr vdp_; + std::unique_ptr vdp_; Concurrency::DeferringAsyncTaskQueue audio_queue_; TI::SN76489 sn76489_; diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 6c166a08b..f26f7ce9a 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -219,7 +219,7 @@ class ConcreteMachine: } void setup_output(float aspect_ratio) override { - vdp_.reset(new TI::TMS9918(TI::TMS9918::TMS9918A)); + vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A)); } void close_output() override { @@ -683,7 +683,7 @@ class ConcreteMachine: }; CPU::Z80::Processor z80_; - std::unique_ptr vdp_; + std::unique_ptr vdp_; Intel::i8255::i8255 i8255_; Concurrency::DeferringAsyncTaskQueue audio_queue_;