summaryrefslogtreecommitdiff
path: root/vdp.hpp
blob: c23927398a5d92b93d69c3a2b562586bf2efcf08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/* SPDX-License-Identifier: Unlicense
 */

#pragma once

#include "bus.hpp"

#include <cstddef>
#include <cstring>

class VDP {
    public:
        constexpr VDP(const uint32_t base_address_a = VDP_START): base_address(base_address_a) {}
        uint32_t Read(uint32_t offset, enum bitness);
        void Write(uint32_t offset, enum bitness, uint32_t value);
        bool Scanline(); // Returns true if display disabled or vblank happened
        void Reset();
        constexpr const uint32_t* GetRenderedBuffer() const { return _rendered_buffer; }

        const uint32_t base_address;

        static constexpr size_t kRenderHeight = 224;
        static constexpr size_t kRenderWidth = 320;
        static constexpr size_t render_buffer_size = kRenderWidth * kRenderHeight *
            sizeof(*reinterpret_cast<VDP*>(4)->GetRenderedBuffer());

    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,
        };

        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
        };

        void renderScrollALine(size_t line_index, size_t hcell_count);
        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);
        const char* regIDToString(RegID);

        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<size_t>(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]{};
};