This repository has been archived by the owner on Dec 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvdp.c
365 lines (314 loc) · 9.25 KB
/
vdp.c
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
#include <string.h>
#include "vdp.h"
#include "font.h"
#include "mw/util.h"
/// VDP shadow register values.
static uint8_t vdpRegShadow[VDP_REG_MAX];
static uint16_t palShadow[4][16];
/// Mask used to build control port data for VDP RAM writes.
/// Bits 15 and 14: CD1 and CD0.
/// Bits 7 to 4: CD5 to CD2.
const uint16_t cdMask[VDP_RAM_TYPE_MAX] = {
0x0000, // VRAM read
0x4000, // VRAM write
0x0020, // CRAM read
0xC000, // CRAM write
0x0010, // VSRAM read
0x4010, // VSRAM write
0x0030, // VRAM read, 8-bit (undocumented)
};
/// Default values for VDP registers
static const uint8_t vdpRegDefaults[19] = {
// Mode 1:
// - No 8 left pixels blank
// - No HINT
0x04,
// Mode 2:
// - Display disabled
// - VBlank int enabled
// - DMA disabled
// - 224 lines
// - Megadrive mode
0x24,
// Name table for PLANE A
VDP_PLANEA_ADDR>>10,
// Name table for WINDOW
VDP_WIN_ADDR>>10,
// Name table for PLANE B
VDP_PLANEB_ADDR>>13,
// Empty sprite attribute table
0x00,
// Empty sprite pattern generator
0x00,
// Background color palette 0, color 0
0x00,
// Unused
0x00,
// Unused
0x00,
// H-Interrupt register
0xFF,
// Mode 3:
// - External interrupt disable
// - No vertical tile scroll
// - Plane horizontal scroll
0x00,
// Mode 4:
// - 40 tiles per line
// - VSync signal
// - HSync signal
// - Standard color data generation
// - Shadow/highlight disabled
// - No interlace
0x81,
// Horizontal scroll data address
VDP_HSCROLL_ADDR>>10,
// Nametable pattern generator
0x00,
// Set auto-increment to 2
0x02,
// Set plane sizes to 128x32 cells
0x03,
// Window H and V positions
0x00, 0x00
};
/************************************************************************//**
* Write to an VDP register, updating the register shadow value.
*
* \param[in] reg Register number (using VdpReg enumerate recommended).
* \param[in] value Value to write to the VDP register.
****************************************************************************/
static inline void VdpRegWrite(uint8_t reg, uint8_t value) {
vdpRegShadow[reg] = value;
VDP_CTRL_PORT_W = 0x8000 | (reg<<8) | value;
}
void VdpInit(void) {
uint16_t i;
for (i = 0; i < sizeof(vdpRegDefaults); i++)
VdpRegWrite(i, vdpRegDefaults[i]);
memset(palShadow, 0, sizeof(palShadow));
// Clear CRAM
VdpRamRwPrep(VDP_CRAM_WR, 0);
for (i = 64; i > 0; i--) VDP_DATA_PORT_W = 0;
// Clear VRAM
/// \bug I do not know why, but VRam Fill does not work. I suspect it
/// has something to do with transfer length.
// VdpDmaVRamFill(0, 0, 0);
// VdpDmaWait();
VdpRamRwPrep(VDP_VRAM_WR, 0);
for (i = 32768; i > 0; i--) VDP_DATA_PORT_W = 0;
// Load font three times, to be able to use three different colors
VdpFontLoad(font, FONT_NCHARS, 0, 1, 0);
VdpFontLoad(font, FONT_NCHARS, FONT_NCHARS * 32, 2, 0);
VdpFontLoad(font, FONT_NCHARS, 2 * FONT_NCHARS * 32, 3, 0);
// Set background and font colors
VdpRamRwPrep(VDP_CRAM_WR, 0);
VDP_DATA_PORT_W = VDP_COLOR_BLACK; // Background color
palShadow[0][0] = VDP_COLOR_BLACK;
VDP_DATA_PORT_W = VDP_COLOR_WHITE; // Text color 1
palShadow[0][1] = VDP_COLOR_BLACK;
VDP_DATA_PORT_W = VDP_COLOR_CYAN; // Text color 2
palShadow[0][2] = VDP_COLOR_BLACK;
VDP_DATA_PORT_W = VDP_COLOR_MAGENTA; // Text color 3
palShadow[0][3] = VDP_COLOR_BLACK;
// Set scroll to 0
VDP_DATA_PORT_W = 0;
VdpRamRwPrep(VDP_VSRAM_WR, 0);
VDP_DATA_PORT_W = 0;
VDP_DATA_PORT_W = 0;
// Set auto-increment to 2
VdpRegWrite(VDP_REG_INCR, 0x02);
// Enable display
VdpRegWrite(VDP_REG_MODE2, 0x74);
}
void VdpDrawText(uint16_t planeAddr, uint8_t x, uint8_t y, uint8_t txtColor,
uint8_t maxChars, const char *text, char fillChar) {
uint16_t offset;
uint16_t i;
// Set auto increment
VdpRegWrite(VDP_REG_INCR, 0x02);
// Calculate nametable offset and prepare VRAM writes
offset = planeAddr + 2 *(x + y * VDP_PLANE_HTILES);
VdpRamRwPrep(VDP_VRAM_WR, offset);
for (i = 0; text[i] && i < maxChars; i++) {
VDP_DATA_PORT_W = text[i] - ' ' + txtColor;
}
while (fillChar && i < maxChars) {
VDP_DATA_PORT_W = fillChar - ' ' + txtColor;
i++;
}
}
void VdpDrawChars(uint16_t planeAddr, uint8_t x, uint8_t y, uint8_t txtColor,
uint8_t numChars, const char *text) {
uint16_t offset;
uint16_t i;
// Set auto increment
VdpRegWrite(VDP_REG_INCR, 0x02);
// Calculate nametable offset and prepare VRAM writes
offset = planeAddr + 2 *(x + y * VDP_PLANE_HTILES);
VdpRamRwPrep(VDP_VRAM_WR, offset);
for (i = 0; i < numChars; i++) {
VDP_DATA_PORT_W = text[i] - ' ' + txtColor;
}
}
void VdpDrawHex(uint16_t planeAddr, uint8_t x, uint8_t y, uint8_t txtColor,
uint8_t num) {
uint16_t offset;
uint8_t tmp;
// Set auto increment
VdpRegWrite(VDP_REG_INCR, 0x02);
// Calculate nametable offset and prepare VRAM writes
offset = planeAddr + 2 * (x + y * VDP_PLANE_HTILES);
VdpRamRwPrep(VDP_VRAM_WR, offset);
// Write hex byte
tmp = num>>4;
VDP_DATA_PORT_W = txtColor + (tmp > 9?tmp - 10 + 0x21:tmp + 0x10);
tmp = num & 0xF;
VDP_DATA_PORT_W = txtColor + (tmp > 9?tmp - 10 + 0x21:tmp + 0x10);
}
uint8_t VdpDrawDec(uint16_t planeAddr, uint8_t x, uint8_t y, uint8_t txtColor,
uint8_t num) {
uint16_t offset;
uint8_t len, i;
char str[4];
// Calculate nametable offset and prepare VRAM writes
offset = planeAddr + 2 * (x + y * VDP_PLANE_HTILES);
VdpRamRwPrep(VDP_VRAM_WR, offset);
len = uint8_to_str(num, str);
for (i = 0; i < len; i++) VDP_DATA_PORT_W = txtColor + 0x10 - '0' + str[i];
return i;
}
void VdpDrawU32(uint16_t planeAddr, uint8_t x, uint8_t y, uint8_t txtColor,
uint32_t num) {
int8_t i;
for (i = 24; i >= 0; i -= 8) {
VdpDrawHex(planeAddr, x, y, txtColor, num>>i);
x += 2;
}
}
void VdpMapLoad(const uint16_t *map, const uint16_t vram_addr, uint8_t map_width,
uint8_t map_height, uint8_t plane_width,
uint16_t tile_offset, uint8_t pal_num)
{
uint8_t i, j;
for (i = 0; i < map_height; i++) {
VdpRamRwPrep(VDP_VRAM_WR, vram_addr + i * plane_width * 2);
for (j = 0; j < map_width; j++) {
VDP_DATA_PORT_W = map[i * map_width + j] +
tile_offset + (pal_num<<13);
}
}
}
void VdpFontLoad(const uint32_t *font, uint8_t chars, uint16_t addr,
uint8_t fgcol, uint8_t bgcol) {
uint32_t line;
uint32_t scratch;
int16_t i;
int8_t j, k;
// Set auto increment
VdpRegWrite(VDP_REG_INCR, 0x02);
// Prepare write
VdpRamRwPrep(VDP_VRAM_WR, addr);
// Note font is 1bpp, and it expanded to 4bpp during load
// Each char takes two DWORDs
for (i = 0; i < 2 * chars; i++) {
line = font[i];
// Process a 32-bit font input
for (j = 0; j < 4; j++) {
scratch = 0;
// Process 8 bits to create 1 VDP DWORD
for (k = 0; k < 8; k++) {
scratch<<=4;
scratch |= line & 1?fgcol:bgcol;
line>>=1;
}
// Write 32-bit value to VDP
VDP_DATA_PORT_DW = scratch;
}
}
}
void VdpDma(uint32_t src, uint16_t dst, uint16_t wLen, uint16_t mem) {
uint32_t cmd; // Command word
// Write transfer length
VdpRegWrite(VDP_REG_DMALEN1, wLen);
VdpRegWrite(VDP_REG_DMALEN2, wLen>>8);
// Write source
VdpRegWrite(VDP_REG_DMASRC1, src>>1);
VdpRegWrite(VDP_REG_DMASRC2, src>>9);
VdpRegWrite(VDP_REG_DMASRC3, src>>17);
// Write command and start DMA
cmd = (dst>>14) | (mem & 0xFF) | (((dst & 0x3FFF) | (mem & 0xFF00))<<16);
VDP_CTRL_PORT_DW = cmd;
}
void VdpDmaVRamFill(uint16_t dst, uint16_t len, uint16_t incr, uint16_t fill) {
uint32_t cmd; // Command word
// Set auto-increment to 1 byte
VdpRegWrite(VDP_REG_INCR, incr);
// Write transfer length
VdpRegWrite(VDP_REG_DMALEN1, len);
VdpRegWrite(VDP_REG_DMALEN2, len>>8);
// Enable DMA fill
VdpRegWrite(VDP_REG_DMASRC3, VDP_DMA_FILL);
// Write destination address
cmd = (dst>>14) | VDP_DMA_FILL | (((dst & 0x7FFF) | 0x4000)<<16);
VDP_CTRL_PORT_DW = cmd;
// Write fill data
VDP_DATA_PORT_W = fill;
}
void VdpDmaVRamCopy(uint16_t src, uint16_t dst, uint16_t len) {
uint32_t cmd; // Command word
// Set auto-increment to 1 byte
VdpRegWrite(VDP_REG_INCR, 0x01);
// Write transfer length
VdpRegWrite(VDP_REG_DMALEN1, len);
VdpRegWrite(VDP_REG_DMALEN2, len>>8);
// Write source
VdpRegWrite(VDP_REG_DMASRC1, src);
VdpRegWrite(VDP_REG_DMASRC2, src>>8);
VdpRegWrite(VDP_REG_DMASRC3, VDP_DMA_COPY);
// Write destination and start transfer
cmd = (dst>>14) | VDP_DMA_COPY | ((dst & 0x3FFF)<<16);
VDP_CTRL_PORT_DW = cmd;
}
void VdpLineClear(uint16_t planeAddr, uint8_t line) {
uint16_t start;
// Calculate nametable offset and prepare VRAM writes
start = planeAddr + 2 * (line * VDP_PLANE_HTILES);
VdpDmaVRamFill(start, 40 * 2, 1, 0);
}
void VdpVBlankWait(void) {
while ((VDP_CTRL_PORT_W & VDP_STAT_VBLANK));
while ((VDP_CTRL_PORT_W & VDP_STAT_VBLANK) == 0);
}
void VdpFramesWait(uint16_t frames) {
while (frames--) VdpVBlankWait();
}
void VdpDisable(void)
{
VdpRegWrite(VDP_REG_MODE2, vdpRegShadow[VDP_REG_MODE2] & (~0x40));
}
void VdpEnable(void)
{
VdpRegWrite(VDP_REG_MODE2, vdpRegShadow[VDP_REG_MODE2] | 0x40);
}
void VdpPalLoad(const uint16_t *pal, uint8_t pal_no)
{
VdpDma((uint32_t)pal, pal_no * 32, 16, VDP_DMA_MEM_CRAM);
memcpy(palShadow[pal_no], pal, 16 * sizeof(uint16_t));
}
const uint16_t *VdpPalGet(uint8_t pal_no)
{
return palShadow[pal_no];
}
void VdpPalFadeOut(uint8_t pal_no)
{
uint16_t r, g, b;
for (int i = 15; i >= 0; i--) {
VdpToRGB(palShadow[pal_no][i], r, g, b);
if (r) r--;
if (g) g--;
if (b) b--;
palShadow[pal_no][i] = VdpColor(r, g, b);
}
VdpDma((uint32_t)palShadow[pal_no], pal_no * 32, 16, VDP_DMA_MEM_CRAM);
}