src/ds/video.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/ds/video.h>
7
8#include <mgba/core/sync.h>
9#include <mgba/internal/arm/macros.h>
10#include <mgba/internal/ds/ds.h>
11#include <mgba/internal/ds/memory.h>
12#include <mgba/internal/gba/video.h>
13
14#include <mgba-util/memory.h>
15
16mLOG_DEFINE_CATEGORY(DS_VIDEO, "DS Video", "ds.video");
17
18static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer);
19static void DSVideoDummyRendererReset(struct DSVideoRenderer* renderer);
20static void DSVideoDummyRendererDeinit(struct DSVideoRenderer* renderer);
21static uint16_t DSVideoDummyRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
22static void DSVideoDummyRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
23static void DSVideoDummyRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
24static void DSVideoDummyRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
25static void DSVideoDummyRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
26static void DSVideoDummyRendererDrawScanlineDirectly(struct DSVideoRenderer* renderer, int y, color_t* scanline);
27static void DSVideoDummyRendererFinishFrame(struct DSVideoRenderer* renderer);
28static void DSVideoDummyRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
29static void DSVideoDummyRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
30
31static void _startHblank7(struct mTiming*, void* context, uint32_t cyclesLate);
32static void _startHdraw7(struct mTiming*, void* context, uint32_t cyclesLate);
33static void _startHblank9(struct mTiming*, void* context, uint32_t cyclesLate);
34static void _startHdraw9(struct mTiming*, void* context, uint32_t cyclesLate);
35
36static const uint32_t _vramSize[9] = {
37 0x20000,
38 0x20000,
39 0x20000,
40 0x20000,
41 0x10000,
42 0x04000,
43 0x04000,
44 0x08000,
45 0x04000
46};
47
48static const uint16_t _zeroes[0x10000] = {0};
49
50enum DSVRAMBankMode {
51 MODE_A_BG = 0,
52 MODE_B_BG = 1,
53 MODE_A_OBJ = 2,
54 MODE_B_OBJ = 3,
55 MODE_LCDC,
56 MODE_7_VRAM,
57 MODE_A_BG_EXT_PAL,
58 MODE_B_BG_EXT_PAL,
59 MODE_A_OBJ_EXT_PAL,
60 MODE_B_OBJ_EXT_PAL,
61 MODE_3D_TEX,
62 MODE_3D_TEX_PAL,
63};
64
65const struct DSVRAMBankInfo {
66 int base;
67 uint32_t mirrorSize;
68 enum DSVRAMBankMode mode;
69 int offset[4];
70} _vramInfo[9][8] = {
71 { // A
72 { 0x000, 0x40, MODE_LCDC },
73 { 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } },
74 { 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x08, 0x80, 0x80 } },
75 { 0x000, 0x01, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } },
76 },
77 { // B
78 { 0x008, 0x40, MODE_LCDC },
79 { 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } },
80 { 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x08, 0x80, 0x80 } },
81 { 0x000, 0x01, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } },
82 },
83 { // C
84 { 0x010, 0x40, MODE_LCDC },
85 { 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } },
86 { 0x000, 0x40, MODE_7_VRAM, { 0x00, 0x08, 0x80, 0x80 } },
87 { 0x000, 0x01, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } },
88 { 0x000, 0x08, MODE_B_BG },
89 },
90 { // D
91 { 0x018, 0x40, MODE_LCDC },
92 { 0x000, 0x20, MODE_A_BG, { 0x00, 0x08, 0x10, 0x18 } },
93 { 0x000, 0x40, MODE_7_VRAM, { 0x00, 0x08, 0x80, 0x80 } },
94 { 0x000, 0x01, MODE_3D_TEX, { 0x00, 0x01, 0x02, 0x03 } },
95 { 0x000, 0x08, MODE_B_OBJ },
96 },
97 { // E
98 { 0x020, 0x40, MODE_LCDC },
99 { 0x000, 0x20, MODE_A_BG },
100 { 0x000, 0x10, MODE_A_OBJ },
101 { 0x000, 0x04, MODE_3D_TEX_PAL },
102 { 0x000, 0x04, MODE_A_BG_EXT_PAL },
103 },
104 { // F
105 { 0x024, 0x40, MODE_LCDC },
106 { 0x000, 0x20, MODE_A_BG, { 0x00, 0x01, 0x04, 0x05 } },
107 { 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x01, 0x04, 0x05 } },
108 { 0x000, 0x01, MODE_3D_TEX_PAL, { 0x00, 0x01, 0x04, 0x05 } },
109 { 0x000, 0x02, MODE_A_BG_EXT_PAL, { 0x00, 0x02, 0x00, 0x02 } },
110 { 0x000, 0x01, MODE_A_OBJ_EXT_PAL},
111 },
112 { // G
113 { 0x025, 0x40, MODE_LCDC },
114 { 0x000, 0x20, MODE_A_BG, { 0x00, 0x01, 0x04, 0x05 } },
115 { 0x000, 0x10, MODE_A_OBJ, { 0x00, 0x01, 0x04, 0x05 } },
116 { 0x000, 0x01, MODE_3D_TEX_PAL, { 0x00, 0x01, 0x04, 0x05 } },
117 { 0x000, 0x02, MODE_A_BG_EXT_PAL, { 0x00, 0x02, 0x00, 0x02 } },
118 { 0x000, 0x01, MODE_A_OBJ_EXT_PAL},
119 },
120 { // H
121 { 0x026, 0x40, MODE_LCDC },
122 { 0x000, 0x04, MODE_B_BG },
123 { 0x000, 0x04, MODE_B_BG_EXT_PAL },
124 },
125 { // I
126 { 0x028, 0x40, MODE_LCDC },
127 { 0x002, 0x04, MODE_B_BG },
128 { 0x000, 0x01, MODE_B_OBJ },
129 { 0x000, 0x01, MODE_B_OBJ_EXT_PAL },
130 },
131};
132
133static struct DSVideoRenderer dummyRenderer = {
134 .init = DSVideoDummyRendererInit,
135 .reset = DSVideoDummyRendererReset,
136 .deinit = DSVideoDummyRendererDeinit,
137 .writeVideoRegister = DSVideoDummyRendererWriteVideoRegister,
138 .writePalette = DSVideoDummyRendererWritePalette,
139 .writeOAM = DSVideoDummyRendererWriteOAM,
140 .invalidateExtPal = DSVideoDummyRendererInvalidateExtPal,
141 .drawScanline = DSVideoDummyRendererDrawScanline,
142 .drawScanlineDirectly = DSVideoDummyRendererDrawScanlineDirectly,
143 .finishFrame = DSVideoDummyRendererFinishFrame,
144 .getPixels = DSVideoDummyRendererGetPixels,
145 .putPixels = DSVideoDummyRendererPutPixels,
146};
147
148void DSVideoInit(struct DSVideo* video) {
149 video->renderer = &dummyRenderer;
150 video->vram = NULL;
151 video->frameskip = 0;
152 video->event7.name = "DS7 Video";
153 video->event7.callback = NULL;
154 video->event7.context = video;
155 video->event7.priority = 8;
156 video->event9.name = "DS9 Video";
157 video->event9.callback = NULL;
158 video->event9.context = video;
159 video->event9.priority = 8;
160}
161
162void DSVideoReset(struct DSVideo* video) {
163 video->vcount = 0;
164 video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
165 video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
166
167 video->event7.callback = _startHblank7;
168 video->event9.callback = _startHblank9;
169 mTimingSchedule(&video->p->ds7.timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH);
170 mTimingSchedule(&video->p->ds9.timing, &video->event9, (DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH) * 2);
171
172 video->frameCounter = 0;
173 video->frameskipCounter = 0;
174
175 if (video->vram) {
176 mappedMemoryFree(video->vram, DS_SIZE_VRAM);
177 }
178 video->vram = anonymousMemoryMap(DS_SIZE_VRAM);
179 video->renderer->vram = video->vram;
180
181 video->p->memory.vramBank[0] = &video->vram[0x00000];
182 video->p->memory.vramBank[1] = &video->vram[0x10000];
183 video->p->memory.vramBank[2] = &video->vram[0x20000];
184 video->p->memory.vramBank[3] = &video->vram[0x30000];
185 video->p->memory.vramBank[4] = &video->vram[0x40000];
186 video->p->memory.vramBank[5] = &video->vram[0x48000];
187 video->p->memory.vramBank[6] = &video->vram[0x4A000];
188 video->p->memory.vramBank[7] = &video->vram[0x4C000];
189 video->p->memory.vramBank[8] = &video->vram[0x50000];
190
191 int i;
192 for (i = 0; i < 32; ++i) {
193 video->renderer->vramABG[i] = _zeroes;
194 video->renderer->vramBBG[i] = _zeroes;
195 video->renderer->vramAOBJ[i] = _zeroes;
196 video->renderer->vramBOBJ[i] = _zeroes;
197 }
198 for (i = 0; i < 4; ++i) {
199 video->renderer->vramABGExtPal[i] = NULL;
200 video->renderer->vramBBGExtPal[i] = NULL;
201 }
202 video->renderer->vramAOBJExtPal = NULL;
203 video->renderer->vramBOBJExtPal = NULL;
204
205 video->renderer->deinit(video->renderer);
206 video->renderer->init(video->renderer);
207}
208
209void DSVideoAssociateRenderer(struct DSVideo* video, struct DSVideoRenderer* renderer) {
210 video->renderer->deinit(video->renderer);
211 video->renderer = renderer;
212 renderer->palette = video->palette;
213 renderer->vram = video->vram;
214 memcpy(renderer->vramABG, video->vramABG, sizeof(renderer->vramABG));
215 memcpy(renderer->vramAOBJ, video->vramAOBJ, sizeof(renderer->vramAOBJ));
216 memcpy(renderer->vramABGExtPal, video->vramABGExtPal, sizeof(renderer->vramABGExtPal));
217 renderer->vramAOBJExtPal = video->vramAOBJExtPal;
218 memcpy(renderer->vramBBG, video->vramBBG, sizeof(renderer->vramBBG));
219 memcpy(renderer->vramBOBJ, video->vramBOBJ, sizeof(renderer->vramBOBJ));
220 memcpy(renderer->vramBBGExtPal, video->vramBBGExtPal, sizeof(renderer->vramBBGExtPal));
221 renderer->vramBOBJExtPal = video->vramBOBJExtPal;
222 renderer->oam = &video->oam;
223 renderer->gx = &video->p->gx;
224 video->renderer->init(video->renderer);
225}
226
227void DSVideoDeinit(struct DSVideo* video) {
228 DSVideoAssociateRenderer(video, &dummyRenderer);
229 mappedMemoryFree(video->vram, DS_SIZE_VRAM);
230}
231
232static void _performCapture(struct DSVideo* video, int y) {
233 DSRegisterDISPCAPCNT dispcap = video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_LO >> 1];
234 dispcap |= video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_HI >> 1] << 16;
235 // TODO: Check mode
236 int block = DSRegisterDISPCAPCNTGetWriteBlock(dispcap);
237 if (!video->p->memory.vramMode[block][4]) {
238 return;
239 }
240 uint16_t* vram = &video->vram[0x10000 * block];
241 const color_t* pixelsA;
242 const uint16_t* srcB = NULL;
243 color_t pixels[DS_VIDEO_HORIZONTAL_PIXELS];
244 int width = DS_VIDEO_HORIZONTAL_PIXELS;
245 switch (DSRegisterDISPCAPCNTGetCaptureSize(dispcap)) {
246 case 0:
247 width = DS_VIDEO_HORIZONTAL_PIXELS / 2;
248 break;
249 case 1:
250 if (y >= 64) {
251 return;
252 }
253 case 2:
254 if (y >= 128) {
255 return;
256 }
257 default:
258 break;
259 }
260
261 if (DSRegisterDISPCAPCNTIsSourceA(dispcap)) {
262 // TODO: Process scanline regardless of output type
263 video->p->gx.renderer->getScanline(video->p->gx.renderer, y, &pixelsA);
264 } else {
265 video->renderer->drawScanlineDirectly(video->renderer, y, pixels);
266 pixelsA = pixels;
267 }
268
269 if (DSRegisterDISPCAPCNTGetCaptureSource(dispcap) > 0) {
270 DSRegisterDISPCNT dispcnt = video->p->ds9.memory.io[DS9_REG_A_DISPCNT_HI >> 1] << 16;
271 int block = DSRegisterDISPCNTGetVRAMBlock(dispcnt);
272 if (!video->p->memory.vramMode[block][4]) {
273 return;
274 }
275 srcB = &video->vram[0x10000 * block];
276 }
277
278 uint32_t base = DSRegisterDISPCAPCNTGetWriteOffset(dispcap) * 0x8000;
279 uint32_t readBase = DSRegisterDISPCAPCNTGetReadOffset(dispcap) * 0x8000;
280
281 unsigned weightA = DSRegisterDISPCAPCNTGetEVA(dispcap);
282 unsigned weightB = DSRegisterDISPCAPCNTGetEVB(dispcap);
283
284 if (weightA > 0x10) {
285 weightA = 0x10;
286 }
287 if (weightB > 0x10) {
288 weightB = 0x10;
289 }
290
291 uint32_t pixel;
292 int x;
293 switch (DSRegisterDISPCAPCNTGetCaptureSource(dispcap)) {
294 case 0:
295 for (x = 0; x < width; ++x) {
296 color_t colorA = pixelsA[x];
297 #ifdef COLOR_16_BIT
298 #ifdef COLOR_5_6_5
299 pixel = colorA & 0x1F;
300 pixel |= (colorA & 0xFFC0) >> 1;
301 #else
302 pixel = colorA;
303 #endif
304 #else
305 pixel = (colorA >> 9) & 0x7C00;
306 pixel |= (colorA >> 6) & 0x03E0;
307 pixel |= (colorA >> 3) & 0x001F;
308 #endif
309 pixel |= 0x8000;
310 STORE_16(pixel, ((x + y * width) * 2 + base) & 0x1FFFE, vram);
311 }
312 break;
313 case 1:
314 for (x = 0; x < width; ++x) {
315 LOAD_16(pixel, ((x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2 + readBase) & 0x1FFFE, srcB);
316 STORE_16(pixel, ((x + y * width) * 2 + base) & 0x1FFFE, vram);
317 }
318 break;
319 case 2:
320 case 3:
321 for (x = 0; x < width; ++x) {
322 color_t colorA = pixelsA[x];
323 uint16_t colorB;
324 LOAD_16(colorB, ((x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2 + readBase) & 0x1FFFE, srcB);
325 #ifdef COLOR_16_BIT
326 #ifdef COLOR_5_6_5
327 pixel = colorA & 0x1F;
328 pixel |= (colorA & 0xFFC0) >> 1;
329 #else
330 pixel = colorA;
331 #endif
332 #else
333 pixel = (colorA >> 9) & 0x7C00;
334 pixel |= (colorA >> 6) & 0x03E0;
335 pixel |= (colorA >> 3) & 0x001F;
336 #endif
337
338 uint32_t a = pixel & 0x7C1F;
339 uint32_t b = colorB & 0x7C1F;
340 a |= (pixel & 0x3E0) << 16;
341 b |= (colorB & 0x3E0) << 16;
342 pixel = ((a * weightA + b * weightB) / 16);
343 if (pixel & 0x04000000) {
344 pixel = (pixel & ~0x07E00000) | 0x03E00000;
345 }
346 if (pixel & 0x0020) {
347 pixel = (pixel & ~0x003F) | 0x001F;
348 }
349 if (pixel & 0x8000) {
350 pixel = (pixel & ~0xF800) | 0x7C00;
351 }
352 pixel = (pixel & 0x7C1F) | ((pixel >> 16) & 0x03E0);
353
354 pixel |= 0x8000;
355 STORE_16(pixel, ((x + y * width) * 2 + base) & 0x1FFFE, vram);
356 }
357 break;
358 }
359}
360
361void _startHdraw7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
362 struct DSVideo* video = context;
363 GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
364 dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
365 video->event7.callback = _startHblank7;
366 mTimingSchedule(timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
367
368 video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
369
370 if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
371 dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
372 if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
373 DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VCOUNTER);
374 }
375 } else {
376 dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
377 }
378 video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
379
380 switch (video->vcount) {
381 case DS_VIDEO_VERTICAL_PIXELS:
382 DSDMARunVblank(&video->p->ds7, -cyclesLate);
383 video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
384 if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
385 DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VBLANK);
386 }
387 break;
388 case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
389 video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
390 break;
391 }
392}
393
394void _startHblank7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
395 struct DSVideo* video = context;
396 GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
397 dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
398 video->event7.callback = _startHdraw7;
399 mTimingSchedule(timing, &video->event7, DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
400
401 // Begin Hblank
402 dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
403
404 if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
405 DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_HBLANK);
406 }
407 video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
408}
409
410void _startHdraw9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
411 struct DSVideo* video = context;
412 GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
413 dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
414 video->event9.callback = _startHblank9;
415 mTimingSchedule(timing, &video->event9, (DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH) * 2 - cyclesLate);
416
417 ++video->vcount;
418 if (video->vcount == DS_VIDEO_VERTICAL_TOTAL_PIXELS) {
419 video->vcount = 0;
420 }
421 video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
422
423 if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
424 dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
425 if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
426 DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VCOUNTER);
427 }
428 } else {
429 dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
430 }
431 video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
432
433 // Note: state may be recorded during callbacks, so ensure it is consistent!
434 switch (video->vcount) {
435 case 0:
436 DSFrameStarted(video->p);
437 video->inCapture = DSRegisterDISPCAPCNTIsEnable(video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_HI >> 1] << 16);
438 break;
439 case DS_VIDEO_VERTICAL_PIXELS:
440 DSDMARunVblank(&video->p->ds9, -cyclesLate);
441 video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
442 video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_HI >> 1] = DSRegisterDISPCAPCNTClearEnable(video->p->ds9.memory.io[DS9_REG_DISPCAPCNT_HI >> 1] << 16) >> 16;
443 if (video->frameskipCounter <= 0) {
444 video->renderer->finishFrame(video->renderer);
445 DSGXFlush(&video->p->gx);
446 }
447 if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
448 DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VBLANK);
449 }
450 video->inCapture = false;
451 DSFrameEnded(video->p);
452 --video->frameskipCounter;
453 if (video->frameskipCounter < 0) {
454 mCoreSyncPostFrame(video->p->sync);
455 video->frameskipCounter = video->frameskip;
456 }
457 ++video->frameCounter;
458 break;
459 case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
460 video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
461 break;
462 }
463}
464
465void _startHblank9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
466 struct DSVideo* video = context;
467 GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
468 dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
469 video->event9.callback = _startHdraw9;
470 mTimingSchedule(timing, &video->event9, (DS9_VIDEO_HBLANK_LENGTH * 2) - cyclesLate);
471
472 // Begin Hblank
473 dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
474 if (video->frameskipCounter <= 0) {
475 if (video->vcount < DS_VIDEO_VERTICAL_PIXELS) {
476 video->renderer->drawScanline(video->renderer, video->vcount);
477 }
478 if (video->vcount < DS_VIDEO_VERTICAL_PIXELS - 48) {
479 video->p->gx.renderer->drawScanline(video->p->gx.renderer, video->vcount + 48);
480 }
481 if (video->vcount >= DS_VIDEO_VERTICAL_TOTAL_PIXELS - 48) {
482 video->p->gx.renderer->drawScanline(video->p->gx.renderer, video->vcount + 48 - DS_VIDEO_VERTICAL_TOTAL_PIXELS);
483 }
484 }
485 if (video->inCapture) {
486 _performCapture(video, video->vcount);
487 }
488
489 if (video->vcount < DS_VIDEO_VERTICAL_PIXELS) {
490 DSDMARunHblank(&video->p->ds9, -cyclesLate);
491 }
492 if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
493 DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_HBLANK);
494 }
495 video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
496}
497
498void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value) {
499 dscore->memory.io[DS_REG_DISPSTAT >> 1] &= 0x7;
500 dscore->memory.io[DS_REG_DISPSTAT >> 1] |= value;
501 // TODO: Does a VCounter IRQ trigger on write?
502}
503
504void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value, uint8_t oldValue) {
505 struct DSMemory* memory = &ds->memory;
506 if (value == oldValue) {
507 return;
508 }
509 uint32_t i, j;
510 uint32_t size = _vramSize[index] >> DS_VRAM_OFFSET;
511 struct DSVRAMBankInfo oldInfo = _vramInfo[index][oldValue & 0x7];
512 uint32_t offset = oldInfo.base + oldInfo.offset[(oldValue >> 3) & 3];
513 switch (oldInfo.mode) {
514 case MODE_A_BG:
515 for (j = offset; j < 0x20; j += oldInfo.mirrorSize) {
516 for (i = 0; i < size; ++i) {
517 if (ds->video.vramABG[i + j] == &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]) {
518 ds->video.vramABG[i + j] = _zeroes;
519 ds->video.renderer->vramABG[i + j] = _zeroes;
520 }
521 }
522 }
523 break;
524 case MODE_B_BG:
525 for (j = offset; j < 0x20; j += oldInfo.mirrorSize) {
526 for (i = 0; i < size; ++i) {
527 if (ds->video.vramBBG[i + j] == &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]) {
528 ds->video.vramBBG[i + j] = _zeroes;
529 ds->video.renderer->vramBBG[i + j] = _zeroes;
530 }
531 }
532 }
533 break;
534 case MODE_A_OBJ:
535 for (j = offset; j < 0x20; j += oldInfo.mirrorSize) {
536 for (i = 0; i < size; ++i) {
537 if (ds->video.vramAOBJ[i + j] == &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]) {
538 ds->video.vramAOBJ[i + j] = _zeroes;
539 ds->video.renderer->vramAOBJ[i + j] = _zeroes;
540 }
541 }
542 }
543 break;
544 case MODE_B_OBJ:
545 for (j = offset; j < 0x20; j += oldInfo.mirrorSize) {
546 for (i = 0; i < size; ++i) {
547 if (ds->video.vramBOBJ[i + j] == &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)]) {
548 ds->video.vramBOBJ[i + j] = _zeroes;
549 ds->video.renderer->vramBOBJ[i + j] = _zeroes;
550 }
551 }
552 }
553 break;
554 case MODE_A_BG_EXT_PAL:
555 for (i = 0; i < oldInfo.mirrorSize; ++i) {
556 if (ds->video.vramABGExtPal[offset + i] == &memory->vramBank[index][i << 12]) {
557 ds->video.vramABGExtPal[offset + i] = NULL;
558 ds->video.renderer->vramABGExtPal[offset + i] = NULL;
559 ds->video.renderer->invalidateExtPal(ds->video.renderer, false, false, offset + i);
560 }
561 }
562 break;
563 case MODE_B_BG_EXT_PAL:
564 for (i = 0; i < oldInfo.mirrorSize; ++i) {
565 if (ds->video.vramBBGExtPal[offset + i] == &memory->vramBank[index][i << 12]) {
566 ds->video.vramBBGExtPal[offset + i] = NULL;
567 ds->video.renderer->vramBBGExtPal[offset + i] = NULL;
568 ds->video.renderer->invalidateExtPal(ds->video.renderer, false, true, offset + i);
569 }
570 }
571 break;
572 case MODE_A_OBJ_EXT_PAL:
573 if (ds->video.vramAOBJExtPal == memory->vramBank[index]) {
574 ds->video.vramAOBJExtPal = NULL;
575 ds->video.renderer->vramAOBJExtPal = NULL;
576 ds->video.renderer->invalidateExtPal(ds->video.renderer, true, false, 0);
577 }
578 break;
579 case MODE_B_OBJ_EXT_PAL:
580 if (ds->video.vramBOBJExtPal == memory->vramBank[index]) {
581 ds->video.vramBOBJExtPal = NULL;
582 ds->video.renderer->vramBOBJExtPal = NULL;
583 ds->video.renderer->invalidateExtPal(ds->video.renderer, true, true, 0);
584 }
585 break;
586 case MODE_3D_TEX:
587 if (ds->gx.tex[offset] == memory->vramBank[index]) {
588 ds->gx.tex[offset] = _zeroes;
589 ds->gx.renderer->tex[offset] = _zeroes;
590 ds->gx.renderer->invalidateTex(ds->gx.renderer, offset);
591 }
592 break;
593 case MODE_3D_TEX_PAL:
594 for (i = 0; i < oldInfo.mirrorSize; ++i) {
595 if (ds->gx.texPal[offset + i] == &memory->vramBank[index][i << 13]) {
596 ds->gx.texPal[offset + i] = _zeroes;
597 ds->gx.renderer->texPal[offset + i] = _zeroes;
598 }
599 }
600 break;
601 case MODE_7_VRAM:
602 for (i = 0; i < size; i += 16) {
603 ds->memory.vram7[(offset + i) >> 4] = NULL;
604 }
605 break;
606 case MODE_LCDC:
607 break;
608 }
609
610 struct DSVRAMBankInfo info = _vramInfo[index][value & 0x7];
611 memset(&memory->vramMirror[index], 0, sizeof(memory->vramMirror[index]));
612 memset(&memory->vramMode[index], 0, sizeof(memory->vramMode[index]));
613 if (!(value & 0x80) || !info.mirrorSize) {
614 return;
615 }
616 offset = info.base + info.offset[(value >> 3) & 3];
617 if (info.mode <= MODE_LCDC) {
618 memory->vramMode[index][info.mode] = 0xFFFF;
619 for (j = offset; j < 0x40; j += info.mirrorSize) {
620 for (i = 0; i < size; ++i) {
621 memory->vramMirror[index][i + j] = 1 << index;
622 }
623 }
624 }
625 switch (info.mode) {
626 case MODE_A_BG:
627 for (j = offset; j < 0x20; j += info.mirrorSize) {
628 for (i = 0; i < size; ++i) {
629 ds->video.vramABG[i + j] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
630 ds->video.renderer->vramABG[i + j] = ds->video.vramABG[i + j];
631 }
632 }
633 break;
634 case MODE_B_BG:
635 for (j = offset; j < 0x20; j += info.mirrorSize) {
636 for (i = 0; i < size; ++i) {
637 ds->video.vramBBG[i + j] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
638 ds->video.renderer->vramBBG[i + j] = ds->video.vramBBG[i + j];
639 }
640 }
641 break;
642 case MODE_A_OBJ:
643 for (j = offset; j < 0x20; j += info.mirrorSize) {
644 for (i = 0; i < size; ++i) {
645 ds->video.vramAOBJ[i + j] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
646 ds->video.renderer->vramAOBJ[i + j] = ds->video.vramAOBJ[i + j];
647 }
648 }
649 break;
650 case MODE_B_OBJ:
651 for (j = offset; j < 0x20; j += info.mirrorSize) {
652 for (i = 0; i < size; ++i) {
653 ds->video.vramBOBJ[i + j] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
654 ds->video.renderer->vramBOBJ[i + j] = ds->video.vramBOBJ[i + j];
655 }
656 }
657 break;
658 case MODE_A_BG_EXT_PAL:
659 for (i = 0; i < info.mirrorSize; ++i) {
660 ds->video.vramABGExtPal[offset + i] = &memory->vramBank[index][i << 12];
661 ds->video.renderer->vramABGExtPal[offset + i] = ds->video.vramABGExtPal[offset + i];
662 ds->video.renderer->invalidateExtPal(ds->video.renderer, false, false, offset + i);
663 }
664 break;
665 case MODE_B_BG_EXT_PAL:
666 for (i = 0; i < info.mirrorSize; ++i) {
667 ds->video.vramBBGExtPal[offset + i] = &memory->vramBank[index][i << 12];
668 ds->video.renderer->vramBBGExtPal[offset + i] = ds->video.vramBBGExtPal[offset + i];
669 ds->video.renderer->invalidateExtPal(ds->video.renderer, false, true, offset + i);
670 }
671 break;
672 case MODE_A_OBJ_EXT_PAL:
673 ds->video.vramAOBJExtPal = memory->vramBank[index];
674 ds->video.renderer->vramAOBJExtPal = ds->video.vramAOBJExtPal;
675 ds->video.renderer->invalidateExtPal(ds->video.renderer, true, false, 0);
676 break;
677 case MODE_B_OBJ_EXT_PAL:
678 ds->video.vramBOBJExtPal = memory->vramBank[index];
679 ds->video.renderer->vramBOBJExtPal = ds->video.vramBOBJExtPal;
680 ds->video.renderer->invalidateExtPal(ds->video.renderer, true, true, 0);
681 break;
682 case MODE_3D_TEX:
683 ds->gx.tex[offset] = memory->vramBank[index];
684 ds->gx.renderer->tex[offset] = ds->gx.tex[offset];
685 ds->gx.renderer->invalidateTex(ds->gx.renderer, offset);
686 break;
687 case MODE_3D_TEX_PAL:
688 for (i = 0; i < info.mirrorSize; ++i) {
689 ds->gx.texPal[offset + i] = &memory->vramBank[index][i << 13];
690 ds->gx.renderer->texPal[offset + i] = ds->gx.texPal[offset + i];
691 }
692 break;
693 case MODE_7_VRAM:
694 for (i = 0; i < size; i += 16) {
695 ds->memory.vram7[(offset + i) >> 4] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 5)];
696 }
697 break;
698 case MODE_LCDC:
699 break;
700 }
701}
702
703static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer) {
704 UNUSED(renderer);
705 // Nothing to do
706}
707
708static void DSVideoDummyRendererReset(struct DSVideoRenderer* renderer) {
709 UNUSED(renderer);
710 // Nothing to do
711}
712
713static void DSVideoDummyRendererDeinit(struct DSVideoRenderer* renderer) {
714 UNUSED(renderer);
715 // Nothing to do
716}
717
718static uint16_t DSVideoDummyRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
719 UNUSED(renderer);
720 return value;
721}
722
723static void DSVideoDummyRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
724 UNUSED(renderer);
725 UNUSED(address);
726 UNUSED(value);
727 // Nothing to do
728}
729
730static void DSVideoDummyRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
731 UNUSED(renderer);
732 UNUSED(oam);
733 // Nothing to do
734}
735
736static void DSVideoDummyRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
737 UNUSED(renderer);
738 UNUSED(obj);
739 UNUSED(engB);
740 // Nothing to do
741}
742
743static void DSVideoDummyRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
744 UNUSED(renderer);
745 UNUSED(y);
746 // Nothing to do
747}
748
749static void DSVideoDummyRendererDrawScanlineDirectly(struct DSVideoRenderer* renderer, int y, color_t* scanline) {
750 UNUSED(renderer);
751 UNUSED(y);
752 UNUSED(scanline);
753 // Nothing to do
754}
755
756static void DSVideoDummyRendererFinishFrame(struct DSVideoRenderer* renderer) {
757 UNUSED(renderer);
758 // Nothing to do
759}
760
761static void DSVideoDummyRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
762 UNUSED(renderer);
763 UNUSED(stride);
764 UNUSED(pixels);
765 // Nothing to do
766}
767
768static void DSVideoDummyRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
769 UNUSED(renderer);
770 UNUSED(stride);
771 UNUSED(pixels);
772 // Nothing to do
773}