src/ds/renderers/software.c (view raw)
1/* Copyright (c) 2013-2017 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/renderers/software.h>
7#include "gba/renderers/software-private.h"
8
9#include <mgba/internal/arm/macros.h>
10#include <mgba/internal/ds/gx.h>
11#include <mgba/internal/ds/io.h>
12
13static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer);
14static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer);
15static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer);
16static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
17static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
18static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
19static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
20static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
21static void DSVideoSoftwareRendererDrawScanlineDirectly(struct DSVideoRenderer* renderer, int y, color_t* scanline);
22static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
23static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
24static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
25
26static void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
27static void DSVideoSoftwareRendererDrawBackgroundExt1(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
28static void DSVideoSoftwareRendererDrawBackgroundExt2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
29
30static bool _regenerateExtPalette(struct DSVideoSoftwareRenderer* renderer, bool obj, bool engB, int slot) {
31 color_t* palette;
32 color_t* variantPalette;
33 struct GBAVideoSoftwareRenderer* softwareRenderer;
34 uint16_t* vram;
35 if (!obj) {
36 if (!engB) {
37 palette = &renderer->extPaletteA[slot * 4096];
38 variantPalette = &renderer->variantPaletteA[slot * 4096];
39 softwareRenderer = &renderer->engA;
40 vram = renderer->d.vramABGExtPal[slot];
41 } else {
42 palette = &renderer->extPaletteB[slot * 4096];
43 variantPalette = &renderer->variantPaletteB[slot * 4096];
44 softwareRenderer = &renderer->engB;
45 vram = renderer->d.vramBBGExtPal[slot];
46 }
47 } else {
48 if (!engB) {
49 palette = renderer->objExtPaletteA;
50 variantPalette = renderer->variantPaletteA;
51 softwareRenderer = &renderer->engA;
52 vram = renderer->d.vramAOBJExtPal;
53 } else {
54 palette = renderer->objExtPaletteB;
55 variantPalette = renderer->variantPaletteB;
56 softwareRenderer = &renderer->engB;
57 vram = renderer->d.vramBOBJExtPal;
58 }
59 }
60 if (!vram) {
61 return false;
62 }
63 int i;
64 for (i = 0; i < 4096; ++i) {
65 uint16_t value = vram[i];
66#ifdef COLOR_16_BIT
67#ifdef COLOR_5_6_5
68 unsigned color = 0;
69 color |= (value & 0x001F) << 11;
70 color |= (value & 0x03E0) << 1;
71 color |= (value & 0x7C00) >> 10;
72#else
73 unsigned color = value;
74#endif
75#else
76 unsigned color = 0;
77 color |= (value << 3) & 0xF8;
78 color |= (value << 6) & 0xF800;
79 color |= (value << 9) & 0xF80000;
80 color |= (color >> 5) & 0x070707;
81#endif
82 palette[i] = color;
83 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
84 variantPalette[i] = _brighten(color, softwareRenderer->bldy);
85 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
86 variantPalette[i] = _darken(color, softwareRenderer->bldy);
87 }
88 }
89 if (obj) {
90 softwareRenderer->objExtPalette = palette;
91 softwareRenderer->objExtVariantPalette = variantPalette;
92 } else {
93 if (slot >= 2) {
94 if (GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot - 2].control)) {
95 softwareRenderer->bg[slot - 2].extPalette = palette;
96 softwareRenderer->bg[slot - 2].variantPalette = variantPalette;
97 }
98 } else if (slot < 2 && !GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot].control) ) {
99 softwareRenderer->bg[slot].extPalette = palette;
100 softwareRenderer->bg[slot].variantPalette = variantPalette;
101 }
102 softwareRenderer->bg[slot].extPalette = palette;
103 softwareRenderer->bg[slot].variantPalette = variantPalette;
104 }
105 return true;
106}
107
108static void _updateCharBase(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
109 struct GBAVideoSoftwareRenderer* eng;
110 if (!engB) {
111 eng = &softwareRenderer->engA;
112 } else {
113 eng = &softwareRenderer->engB;
114 }
115 int i;
116 uint32_t charBase = DSRegisterDISPCNTGetCharBase(softwareRenderer->dispcntA) << 16;
117 uint32_t screenBase = DSRegisterDISPCNTGetScreenBase(softwareRenderer->dispcntA) << 16;
118 for (i = 0; i < 4; ++i) {
119 if (!engB) {
120 uint32_t control = eng->bg[i].control;
121 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_BG0CNT + i * 2, control);
122 eng->bg[i].control = control;
123 }
124
125 eng->bg[i].charBase = GBARegisterBGCNTGetCharBase(eng->bg[i].control) << 14;
126
127 if (!engB) {
128 softwareRenderer->engA.bg[i].charBase += charBase;
129 softwareRenderer->engA.bg[i].screenBase &= ~0x70000;
130 softwareRenderer->engA.bg[i].screenBase |= screenBase;
131 }
132 }
133}
134
135void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
136 renderer->d.init = DSVideoSoftwareRendererInit;
137 renderer->d.reset = DSVideoSoftwareRendererReset;
138 renderer->d.deinit = DSVideoSoftwareRendererDeinit;
139 renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
140 renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
141 renderer->d.writeOAM = DSVideoSoftwareRendererWriteOAM;
142 renderer->d.invalidateExtPal = DSVideoSoftwareRendererInvalidateExtPal;
143 renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
144 renderer->d.drawScanlineDirectly = DSVideoSoftwareRendererDrawScanlineDirectly;
145 renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
146 renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
147 renderer->d.putPixels = DSVideoSoftwareRendererPutPixels;
148
149 renderer->engA.d.cache = NULL;
150 GBAVideoSoftwareRendererCreate(&renderer->engA);
151 renderer->engA.combinedObjSort = true;
152
153 renderer->engB.d.cache = NULL;
154 GBAVideoSoftwareRendererCreate(&renderer->engB);
155 renderer->engB.combinedObjSort = true;
156}
157
158static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer) {
159 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
160 softwareRenderer->engA.d.palette = &renderer->palette[0];
161 softwareRenderer->engA.d.oam = &renderer->oam->oam[0];
162 softwareRenderer->engA.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
163 softwareRenderer->engA.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
164 softwareRenderer->engA.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
165 softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride;
166 softwareRenderer->engB.d.palette = &renderer->palette[512];
167 softwareRenderer->engB.d.oam = &renderer->oam->oam[1];
168 softwareRenderer->engB.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
169 softwareRenderer->engB.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
170 softwareRenderer->engB.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
171 softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride;
172
173 DSVideoSoftwareRendererReset(renderer);
174}
175
176static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer) {
177 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
178 softwareRenderer->engA.d.reset(&softwareRenderer->engA.d);
179 softwareRenderer->engB.d.reset(&softwareRenderer->engB.d);
180 softwareRenderer->powcnt = 0;
181 softwareRenderer->dispcntA = 0;
182 softwareRenderer->dispcntB = 0;
183}
184
185static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
186 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
187 softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
188 softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
189}
190
191static void DSVideoSoftwareRendererUpdateDISPCNT(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
192 uint32_t dispcnt;
193 struct GBAVideoSoftwareRenderer* eng;
194 if (!engB) {
195 dispcnt = softwareRenderer->dispcntA;
196 eng = &softwareRenderer->engA;
197 } else {
198 dispcnt = softwareRenderer->dispcntB;
199 eng = &softwareRenderer->engB;
200 }
201 uint16_t fakeDispcnt = dispcnt & 0xFF87;
202 if (!DSRegisterDISPCNTIsTileObjMapping(dispcnt)) {
203 eng->tileStride = 0x20;
204 } else {
205 eng->tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(dispcnt);
206 fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
207 }
208 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
209 eng->dispcnt |= dispcnt & 0xFFFF0000;
210 if (DSRegisterDISPCNTIsBgExtPalette(dispcnt)) {
211 color_t* extPalette;
212 if (!engB) {
213 extPalette = softwareRenderer->extPaletteA;
214 } else {
215 extPalette = softwareRenderer->extPaletteB;
216 }
217 int i;
218 for (i = 0; i < 4; ++i) {
219 int slot = i;
220 if (i < 2 && GBARegisterBGCNTIsExtPaletteSlot(eng->bg[i].control)) {
221 slot += 2;
222 }
223 if (eng->bg[i].extPalette != &extPalette[slot * 4096]) {
224 _regenerateExtPalette(softwareRenderer, false, engB, slot);
225 }
226 }
227 } else {
228 eng->bg[0].extPalette = NULL;
229 eng->bg[1].extPalette = NULL;
230 eng->bg[2].extPalette = NULL;
231 eng->bg[3].extPalette = NULL;
232 }
233 if (DSRegisterDISPCNTIsObjExtPalette(dispcnt)) {
234 if (!engB) {
235 if (softwareRenderer->engA.objExtPalette != softwareRenderer->objExtPaletteA) {
236 _regenerateExtPalette(softwareRenderer, true, engB, 0);
237 }
238 } else {
239 if (softwareRenderer->engB.objExtPalette != softwareRenderer->objExtPaletteB) {
240 _regenerateExtPalette(softwareRenderer, true, engB, 0);
241 }
242 }
243 } else {
244 if (!engB) {
245 softwareRenderer->engA.objExtPalette = NULL;
246 } else {
247 softwareRenderer->engB.objExtPalette = NULL;
248 }
249 }
250 if (!engB) {
251 eng->dispcnt = DSRegisterDISPCNTClear3D(eng->dispcnt);
252 eng->dispcnt |= DSRegisterDISPCNTIs3D(dispcnt);
253 _updateCharBase(softwareRenderer, engB);
254 }
255}
256
257static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
258 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
259 if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
260 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
261 } else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
262 softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
263 } else {
264 mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
265 }
266 switch (address) {
267 case DS9_REG_A_BG0CNT:
268 case DS9_REG_A_BG1CNT:
269 softwareRenderer->engA.bg[(address - DS9_REG_A_BG0CNT) >> 1].control = value;
270 // Fall through
271 case DS9_REG_A_BG2CNT:
272 case DS9_REG_A_BG3CNT:
273 _updateCharBase(softwareRenderer, false);
274 break;
275 case DS9_REG_B_BG0CNT:
276 case DS9_REG_B_BG1CNT:
277 softwareRenderer->engB.bg[(address - DS9_REG_B_BG0CNT) >> 1].control = value;
278 // Fall through
279 case DS9_REG_B_BG2CNT:
280 case DS9_REG_B_BG3CNT:
281 _updateCharBase(softwareRenderer, true);
282 break;
283 case DS9_REG_A_MASTER_BRIGHT:
284 softwareRenderer->engA.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
285 softwareRenderer->engA.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
286 if (softwareRenderer->engA.masterBrightY > 0x10) {
287 softwareRenderer->engA.masterBrightY = 0x10;
288 }
289 break;
290 case DS9_REG_B_MASTER_BRIGHT:
291 softwareRenderer->engB.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
292 softwareRenderer->engB.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
293 if (softwareRenderer->engB.masterBrightY > 0x10) {
294 softwareRenderer->engB.masterBrightY = 0x10;
295 }
296 break;
297 case DS9_REG_A_BLDCNT:
298 case DS9_REG_A_BLDY:
299 // TODO: Optimize
300 _regenerateExtPalette(softwareRenderer, false, false, 0);
301 _regenerateExtPalette(softwareRenderer, false, false, 1);
302 _regenerateExtPalette(softwareRenderer, false, false, 2);
303 _regenerateExtPalette(softwareRenderer, false, false, 3);
304 _regenerateExtPalette(softwareRenderer, true, false, 0);
305 break;
306 case DS9_REG_B_BLDCNT:
307 case DS9_REG_B_BLDY:
308 // TODO: Optimize
309 _regenerateExtPalette(softwareRenderer, false, true, 0);
310 _regenerateExtPalette(softwareRenderer, false, true, 1);
311 _regenerateExtPalette(softwareRenderer, false, true, 2);
312 _regenerateExtPalette(softwareRenderer, false, true, 3);
313 _regenerateExtPalette(softwareRenderer, true, true, 0);
314 break;
315 case DS9_REG_A_DISPCNT_LO:
316 softwareRenderer->dispcntA &= 0xFFFF0000;
317 softwareRenderer->dispcntA |= value;
318 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
319 break;
320 case DS9_REG_A_DISPCNT_HI:
321 softwareRenderer->dispcntA &= 0x0000FFFF;
322 softwareRenderer->dispcntA |= value << 16;
323 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
324 break;
325 case DS9_REG_B_DISPCNT_LO:
326 softwareRenderer->dispcntB &= 0xFFFF0000;
327 softwareRenderer->dispcntB |= value;
328 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
329 break;
330 case DS9_REG_B_DISPCNT_HI:
331 softwareRenderer->dispcntB &= 0x0000FFFF;
332 softwareRenderer->dispcntB |= value << 16;
333 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
334 break;
335 case DS9_REG_POWCNT1:
336 value &= 0x810F;
337 softwareRenderer->powcnt = value;
338 }
339 return value;
340}
341
342static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
343 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
344 if (address < 0x400) {
345 softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
346 } else {
347 softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
348 }
349}
350
351static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
352 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
353 if (oam < 0x200) {
354 softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
355 } else {
356 softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
357 }
358}
359
360static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
361 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
362 _regenerateExtPalette(softwareRenderer, obj, engB, slot);
363}
364
365static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* renderer, struct DSGX* gx, int y) {
366 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
367
368 int x;
369 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
370 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
371 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
372 row[x] = GBA_COLOR_WHITE;
373 }
374 return;
375 }
376
377 GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
378 int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
379 memset(softwareRenderer->alphaA, softwareRenderer->blda, sizeof(softwareRenderer->alphaA));
380 memset(softwareRenderer->alphaB, softwareRenderer->bldb, sizeof(softwareRenderer->alphaB));
381
382 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].dmx * y;
383 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].dmy * y;
384 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].dmx * y;
385 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].dmy * y;
386
387 int w;
388 unsigned priority;
389 for (priority = 0; priority < 4; ++priority) {
390 softwareRenderer->end = 0;
391 for (w = 0; w < softwareRenderer->nWindows; ++w) {
392 softwareRenderer->start = softwareRenderer->end;
393 softwareRenderer->end = softwareRenderer->windows[w].endX;
394 softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
395 if (spriteLayers & (1 << priority)) {
396 GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
397 }
398 if (TEST_LAYER_ENABLED(0)) {
399 if (DSRegisterDISPCNTIs3D(softwareRenderer->dispcnt) && gx) {
400 const color_t* scanline;
401 gx->renderer->getScanline(gx->renderer, y, &scanline);
402 uint32_t flags = (softwareRenderer->bg[0].priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
403 flags |= FLAG_TARGET_2 * softwareRenderer->bg[0].target2;
404 flags |= FLAG_TARGET_1 * (softwareRenderer->bg[0].target1 && softwareRenderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(softwareRenderer->currentWindow.packed));
405 int x;
406 for (x = softwareRenderer->start; x < softwareRenderer->end; ++x) {
407 color_t color = scanline[x];
408 if (color & 0xFC000000) {
409 if (softwareRenderer->bg[0].target1 && GBAWindowControlIsBlendEnable(softwareRenderer->currentWindow.packed)) {
410 if (softwareRenderer->blendEffect == BLEND_DARKEN) {
411 color = _darken(color, softwareRenderer->bldy) | (color & 0xFF000000);
412 } else if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
413 color = _brighten(color, softwareRenderer->bldy) | (color & 0xFF000000);
414 }
415 }
416 if ((scanline[x] >> 28) != 0xF) {
417 // TODO: More precise values
418 softwareRenderer->alphaA[x] = (color >> 28) + 1;
419 softwareRenderer->alphaB[x] = 0xF - (color >> 28);
420 _compositeBlendNoObjwin(softwareRenderer, x, (color & 0x00FFFFFF) | flags | FLAG_TARGET_1, softwareRenderer->row[x]);
421 } else {
422 if (!(flags & FLAG_TARGET_2) || !(softwareRenderer->row[x] & FLAG_TARGET_1)) {
423 _compositeNoBlendNoObjwin(softwareRenderer, x, (color & 0x00FFFFFF) | flags, softwareRenderer->row[x]);
424 softwareRenderer->alphaA[x] = 0x10;
425 softwareRenderer->alphaB[x] = 0;
426 } else if (softwareRenderer->row[x] & FLAG_TARGET_1) {
427 _compositeBlendNoObjwin(softwareRenderer, x, (color & 0x00FFFFFF) | flags, softwareRenderer->row[x]);
428 }
429 }
430 }
431 }
432 } else {
433 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
434 }
435 }
436 if (TEST_LAYER_ENABLED(1)) {
437 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
438 }
439 if (TEST_LAYER_ENABLED(2)) {
440 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
441 case 0:
442 case 1:
443 case 3:
444 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
445 break;
446 case 2:
447 case 4:
448 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
449 break;
450 case 5:
451 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[2].control)) {
452 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[2], y);
453 } else if (!GBARegisterBGCNTIsExtendedMode0(softwareRenderer->bg[2].control)) {
454 DSVideoSoftwareRendererDrawBackgroundExt1(softwareRenderer, &softwareRenderer->bg[2], y);
455 } else {
456 DSVideoSoftwareRendererDrawBackgroundExt2(softwareRenderer, &softwareRenderer->bg[2], y);
457 }
458 break;
459 }
460 }
461 if (TEST_LAYER_ENABLED(3)) {
462 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
463 case 0:
464 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
465 break;
466 case 1:
467 case 2:
468 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
469 break;
470 case 3:
471 case 4:
472 case 5:
473 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[3].control)) {
474 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[3], y);
475 } else if (!GBARegisterBGCNTIsExtendedMode0(softwareRenderer->bg[3].control)) {
476 DSVideoSoftwareRendererDrawBackgroundExt1(softwareRenderer, &softwareRenderer->bg[3], y);
477 } else {
478 DSVideoSoftwareRendererDrawBackgroundExt2(softwareRenderer, &softwareRenderer->bg[3], y);
479 }
480 break;
481 }
482 }
483 }
484 }
485
486 GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
487}
488
489static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
490 memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
491 memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
492 color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
493
494 int x;
495 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
496 case 0:
497 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
498 row[x] = GBA_COLOR_WHITE;
499 }
500 return;
501 case 1:
502 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
503 break;
504 case 2: {
505 uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
506 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
507 color_t color;
508 LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
509#ifndef COLOR_16_BIT
510 unsigned color32 = 0;
511 color32 |= (color << 9) & 0xF80000;
512 color32 |= (color << 3) & 0xF8;
513 color32 |= (color << 6) & 0xF800;
514 color32 |= (color32 >> 5) & 0x070707;
515 color = color32;
516#elif COLOR_5_6_5
517 uint16_t color16 = 0;
518 color16 |= (color & 0x001F) << 11;
519 color16 |= (color & 0x03E0) << 1;
520 color16 |= (color & 0x7C00) >> 10;
521 color = color16;
522#endif
523 softwareRenderer->engA.row[x] = color;
524 }
525 break;
526 }
527 case 3:
528 break;
529 }
530
531#ifdef COLOR_16_BIT
532#if defined(__ARM_NEON) && !defined(__APPLE__)
533 _to16Bit(row, softwareRenderer->engA.row, DS_VIDEO_HORIZONTAL_PIXELS);
534#else
535 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
536 row[x] = softwareRenderer->engA.row[x];
537 }
538#endif
539#else
540 switch (softwareRenderer->engA.masterBright) {
541 case 0:
542 default:
543 memcpy(row, softwareRenderer->engA.row, softwareRenderer->engA.masterEnd * sizeof(*row));
544 break;
545 case 1:
546 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
547 row[x] = _brighten(softwareRenderer->engA.row[x], softwareRenderer->engA.masterBrightY);
548 }
549 break;
550 case 2:
551 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
552 row[x] = _darken(softwareRenderer->engA.row[x], softwareRenderer->engA.masterBrightY);
553 }
554 break;
555 }
556#endif
557}
558
559static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
560 memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
561 memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
562 color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
563
564 int x;
565 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
566 case 0:
567 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
568 row[x] = GBA_COLOR_WHITE;
569 }
570 return;
571 case 1:
572 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engB.d, NULL, y);
573 break;
574 }
575
576#ifdef COLOR_16_BIT
577#if defined(__ARM_NEON) && !defined(__APPLE__)
578 _to16Bit(row, softwareRenderer->engB.row, DS_VIDEO_HORIZONTAL_PIXELS);
579#else
580 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
581 row[x] = softwareRenderer->engB.row[x];
582 }
583#endif
584#else
585 switch (softwareRenderer->engB.masterBright) {
586 case 0:
587 default:
588 memcpy(row, softwareRenderer->engB.row, softwareRenderer->engB.masterEnd * sizeof(*row));
589 break;
590 case 1:
591 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
592 row[x] = _brighten(softwareRenderer->engB.row[x], softwareRenderer->engB.masterBrightY);
593 }
594 break;
595 case 2:
596 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
597 row[x] = _darken(softwareRenderer->engB.row[x], softwareRenderer->engB.masterBrightY);
598 }
599 break;
600 }
601#endif
602}
603
604static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
605 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
606 if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
607 softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
608 softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
609 } else {
610 softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
611 softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
612 }
613
614 _drawScanlineA(softwareRenderer, y);
615 _drawScanlineB(softwareRenderer, y);
616}
617
618static void DSVideoSoftwareRendererDrawScanlineDirectly(struct DSVideoRenderer* renderer, int y, color_t* scanline) {
619 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
620 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
621 memcpy(scanline, softwareRenderer->engA.row, softwareRenderer->engA.masterEnd * sizeof(*scanline));
622}
623
624static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
625 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
626 softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
627 softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
628}
629
630static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
631 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
632#ifdef COLOR_16_BIT
633#error Not yet supported
634#else
635 *stride = softwareRenderer->outputBufferStride;
636 *pixels = softwareRenderer->outputBuffer;
637#endif
638}
639
640static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
641}
642
643#define EXT_0_COORD_OVERFLOW \
644 localX = x & (sizeAdjusted - 1); \
645 localY = y & (sizeAdjusted - 1); \
646
647#define EXT_0_COORD_NO_OVERFLOW \
648 if ((x | y) & ~(sizeAdjusted - 1)) { \
649 continue; \
650 } \
651 localX = x; \
652 localY = y;
653
654#define EXT_0_EXT_PAL \
655 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
656 palette = &mainPalette[paletteData];
657
658#define EXT_0_PAL palette = mainPalette;
659
660#define EXT_0_NO_MOSAIC(COORD, PAL) \
661 COORD \
662 uint32_t screenBase = background->screenBase + (localX >> 10) + (((localY >> 6) & 0xFE0) << background->size); \
663 uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
664 if (UNLIKELY(!screenBlock)) { \
665 continue; \
666 } \
667 LOAD_16(mapData, screenBase & (VRAM_BLOCK_MASK - 1), screenBlock); \
668 PAL; \
669 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
670 localY = 0x7FF - localY; \
671 } \
672 if (GBA_TEXT_MAP_HFLIP(mapData)) { \
673 localX = 0x7FF - localX; \
674 } \
675 uint32_t charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8); \
676 uint16_t* vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
677 if (UNLIKELY(!vram)) { \
678 continue; \
679 } \
680 pixelData = ((uint8_t*) vram)[charBase & VRAM_BLOCK_MASK];
681
682#define EXT_0_MOSAIC(COORD, PAL) \
683 if (!mosaicWait) { \
684 EXT_0_NO_MOSAIC(COORD, PAL) \
685 mosaicWait = mosaicH; \
686 } else { \
687 --mosaicWait; \
688 }
689
690#define EXT_0_LOOP(MOSAIC, COORD, PAL, BLEND, OBJWIN) \
691 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
692 x += background->dx; \
693 y += background->dy; \
694 \
695 uint32_t current = *pixel; \
696 MOSAIC(COORD, PAL) \
697 if (pixelData) { \
698 COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
699 } \
700 }
701
702#define DRAW_BACKGROUND_EXT_0(BLEND, OBJWIN) \
703 if (background->extPalette) { \
704 if (background->overflow) { \
705 if (mosaicH > 1) { \
706 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
707 } else { \
708 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
709 } \
710 } else { \
711 if (mosaicH > 1) { \
712 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
713 } else { \
714 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
715 } \
716 } \
717 } else { \
718 if (background->overflow) { \
719 if (mosaicH > 1) { \
720 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
721 } else { \
722 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
723 } \
724 } else { \
725 if (mosaicH > 1) { \
726 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
727 } else { \
728 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
729 } \
730 } \
731 }
732
733void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
734 int sizeAdjusted = 0x8000 << background->size;
735
736 BACKGROUND_BITMAP_INIT;
737
738 color_t* mainPalette = background->extPalette;
739 if (variant) {
740 mainPalette = background->variantPalette;
741 }
742 if (!mainPalette) {
743 mainPalette = renderer->normalPalette;
744 if (variant) {
745 mainPalette = renderer->variantPalette;
746 }
747 }
748 int paletteData;
749
750 uint16_t mapData;
751 uint8_t pixelData = 0;
752
753 int outX;
754 uint32_t* pixel;
755
756 if (!objwinSlowPath) {
757 if (!(flags & FLAG_TARGET_2)) {
758 DRAW_BACKGROUND_EXT_0(NoBlend, NO_OBJWIN);
759 } else {
760 DRAW_BACKGROUND_EXT_0(Blend, NO_OBJWIN);
761 }
762 } else {
763 if (!(flags & FLAG_TARGET_2)) {
764 DRAW_BACKGROUND_EXT_0(NoBlend, OBJWIN);
765 } else {
766 DRAW_BACKGROUND_EXT_0(Blend, OBJWIN);
767 }
768 }
769}
770
771#define DS_BACKGROUND_BITMAP_ITERATE(W, H) \
772 x += background->dx; \
773 y += background->dy; \
774 \
775 if (background->overflow) { \
776 localX = x & ((W << 8) - 1); \
777 localY = y & ((H << 8) - 1); \
778 } else if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
779 continue; \
780 } else { \
781 localX = x; \
782 localY = y; \
783 }
784
785void DSVideoSoftwareRendererDrawBackgroundExt1(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
786 BACKGROUND_BITMAP_INIT;
787
788 uint32_t screenBase = (background->screenBase & 0xFF00) * 8;
789 uint8_t color;
790 int width, height;
791 switch (background->size) {
792 case 0:
793 width = 128;
794 height = 128;
795 break;
796 case 1:
797 width = 256;
798 height = 256;
799 break;
800 case 2:
801 width = 512;
802 height = 256;
803 break;
804 case 3:
805 width = 512;
806 height = 512;
807 break;
808 }
809
810 int outX;
811 for (outX = renderer->start; outX < renderer->end; ++outX) {
812 DS_BACKGROUND_BITMAP_ITERATE(width, height);
813
814 if (!mosaicWait) {
815 uint32_t address = (localX >> 8) + (localY >> 8) * width + screenBase;
816 uint8_t* vram = (uint8_t*) renderer->d.vramBG[address >> VRAM_BLOCK_OFFSET];
817 if (UNLIKELY(!vram)) {
818 continue;
819 }
820 color = vram[address & VRAM_BLOCK_MASK];
821 mosaicWait = mosaicH;
822 } else {
823 --mosaicWait;
824 }
825
826 uint32_t current = renderer->row[outX];
827 if (color && IS_WRITABLE(current)) {
828 if (!objwinSlowPath) {
829 _compositeBlendNoObjwin(renderer, outX, palette[color] | flags, current);
830 } else if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) {
831 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
832 unsigned mergedFlags = flags;
833 if (current & FLAG_OBJWIN) {
834 mergedFlags = objwinFlags;
835 }
836 _compositeBlendObjwin(renderer, outX, currentPalette[color] | mergedFlags, current);
837 }
838 }
839 }
840}
841
842void DSVideoSoftwareRendererDrawBackgroundExt2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
843 BACKGROUND_BITMAP_INIT;
844
845 uint32_t screenBase = (background->screenBase & 0xFF00) * 4;
846 uint32_t color;
847 int width, height;
848 switch (background->size) {
849 case 0:
850 width = 128;
851 height = 128;
852 break;
853 case 1:
854 width = 256;
855 height = 256;
856 break;
857 case 2:
858 width = 512;
859 height = 256;
860 break;
861 case 3:
862 width = 512;
863 height = 512;
864 break;
865 }
866
867 int outX;
868 for (outX = renderer->start; outX < renderer->end; ++outX) {
869 DS_BACKGROUND_BITMAP_ITERATE(width, height);
870
871 if (!mosaicWait) {
872 uint32_t address = ((localX >> 8) + (localY >> 8) * width + screenBase) << 1;
873 uint16_t* vram = renderer->d.vramBG[address >> VRAM_BLOCK_OFFSET];
874 if (UNLIKELY(!vram)) {
875 continue;
876 }
877 LOAD_16(color, address & VRAM_BLOCK_MASK, vram);
878#ifndef COLOR_16_BIT
879 unsigned color32;
880 color32 = 0;
881 color32 |= (color << 3) & 0xF8;
882 color32 |= (color << 6) & 0xF800;
883 color32 |= (color << 9) & 0xF80000;
884 color32 |= (color32 >> 5) & 0x070707;
885 color = color32;
886#elif COLOR_5_6_5
887 uint16_t color16 = 0;
888 color16 |= (color & 0x001F) << 11;
889 color16 |= (color & 0x03E0) << 1;
890 color16 |= (color & 0x7C00) >> 10;
891 color = color16;
892#endif
893 mosaicWait = mosaicH;
894 } else {
895 --mosaicWait;
896 }
897
898 uint32_t current = renderer->row[outX];
899 if (!objwinSlowPath || (!(current & FLAG_OBJWIN)) != objwinOnly) {
900 unsigned mergedFlags = flags;
901 if (current & FLAG_OBJWIN) {
902 mergedFlags = objwinFlags;
903 }
904 if (!variant) {
905 _compositeBlendObjwin(renderer, outX, color | mergedFlags, current);
906 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
907 _compositeBlendObjwin(renderer, outX, _brighten(color, renderer->bldy) | mergedFlags, current);
908 } else if (renderer->blendEffect == BLEND_DARKEN) {
909 _compositeBlendObjwin(renderer, outX, _darken(color, renderer->bldy) | mergedFlags, current);
910 }
911 }
912 }
913}