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