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