summaryrefslogtreecommitdiff
path: root/vdp.hpp
blob: ab8c3373f6d4c695a53d4011032288e0ef4a4729 (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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* SPDX-License-Identifier: Unlicense
 */

#pragma once

#include "bus.hpp"

#include <cstddef>
#include <cstring>

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 = 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; }
    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<VDP*>(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);

    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]{};
    bool _unclosed_sprites_list{};
};