/* SPDX-License-Identifier: Unlicense */ #pragma once #include "bus.hpp" #include #include #include struct SpriteAttributeEntry { uint16_t vpos; uint16_t hpos; uint16_t tile_id; uint8_t link; uint8_t palette_id; uint8_t priority; uint8_t vreverse; uint8_t hreverse; uint8_t vsize; uint8_t hsize; }; class VDP { public: enum class RegID: size_t { kModeSet1 = 0, kModeSet2 = 1, kScrollAAddress = 2, kWindowAddress = 3, kScrollBAddress = 4, kSpritesTableAddress = 5, kBackgroundColor = 7, kHint = 10, kModeSet3 = 11, kModeSet4 = 12, kHScrollTableAddress = 13, kAutoIncrement = 15, kScrollSize = 16, kWindowHorizontalPosition = 17, kWindowVertialPosition = 18, kDMALengthCounterLow = 19, kDMALengthCounterHigh = 20, kDMASourceAddressLow = 21, kDMASourceAddressMid = 22, kDMASourceAddressHigh = 23, kRegistersCount, ///< Keep it last }; constexpr VDP(const uint32_t base_address_a): base_address(base_address_a) {} uint32_t Read(uint32_t offset, enum bitness); void Write(uint32_t offset, enum bitness, uint32_t value); /** Renders a horizontal line (scan line). * * Returns true if display disabled or VBLANK could've had happened, even if * VBLANK is disabled (MODESET2_IE0 is 0). * */ bool Scanline(); void Reset(); constexpr const uint32_t* GetRenderedBuffer() const { return _rendered_buffer; } static const char* RegIDToString(RegID); static constexpr size_t kRenderHeight = 224; static constexpr size_t kRenderWidth = 320; static constexpr size_t render_buffer_size = kRenderWidth * kRenderHeight * sizeof(*reinterpret_cast(4)->GetRenderedBuffer()); const uint32_t base_address; private: struct StatusRegister { bool fifo_not_empty{}; bool fifo_full{}; bool v_irq{}; bool sprites_overflow{}; bool sprites_collision{}; bool odd_frame{}; bool vblank{}; bool hblank{}; bool dma_busy{}; bool pal_mode{}; }; enum class AddressMode: uint8_t { kVRAMWrite = 1, kCRAMWrite = 3, kVSRAMWrite = 5, kVRAMRead = 0, kCRAMRead = 8, kVSRAMRead = 4, }; void renderScrollLine( size_t line_index, uint16_t plane_addr, uint16_t hscroll_table_addr, size_t plane_index, size_t hcell_count, size_t vcell_count); void renderScrollALine(size_t line_index, size_t hcell_count, size_t vcell_count); void renderScrollBLine(size_t line_index, size_t hcell_count, size_t vcell_count); void renderSpritesLine(size_t line_index); void renderSpriteOnTheLine(SpriteAttributeEntry, size_t line_index); void writeData(uint8_t address_mode, uint16_t address, uint16_t value); void writeControl(uint16_t value); uint16_t readData(uint8_t address_mode, uint16_t address); uint16_t readStatusRegister() const; uint8_t* baseFromAddressMode(uint8_t address_mode); const char* addressModeToString(uint8_t address_mode); void traceRegWrite(FILE* const s, const uint8_t reg, const uint8_t val) const; static void runDMAMemoryToVRAM( uint8_t* base, uint32_t source_address, uint16_t destination_address, uint16_t transfer_size, uint8_t increment); static void runDMAVRAMFill( uint8_t* base, uint16_t destination_address, uint16_t transfer_size, uint8_t increment, uint16_t filler); static void runDMAVRAMCopy(uint8_t* base); static constexpr size_t kRegistersCount = static_cast(RegID::kRegistersCount); static constexpr size_t kVRAMSize = 64*1024; static constexpr size_t kCRAMSize = 64*2; static constexpr size_t kVSRAMSize = 40*2; static constexpr uint16_t kLinesPerScreenNTSC = 262; static constexpr uint16_t kLinesPerScreenPAL = 312; StatusRegister _status{}; bool _control_write_second_word{}; // DMA Fill is ready to kick in, waiting for filler set to data port bool _dma_ready{}; uint16_t _lines_counter{}; // TODO: Make _address_mode of type AddressMode somehow and all private // methods using it should be accepting AddressMode instead of uint8_t. uint8_t _address_mode{}; uint16_t _address{}; uint8_t _reg[kRegistersCount]{}; uint8_t _vram[kVRAMSize]{}; uint8_t _cram[kCRAMSize]{}; uint8_t _vsram[kVSRAMSize]{}; uint32_t _rendered_buffer[kLinesPerScreenNTSC * kRenderWidth]{}; bool _unclosed_sprites_list{}; };