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