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