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 DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
22static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
23static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
24
25static void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
26
27static bool _regenerateExtPalette(struct DSVideoSoftwareRenderer* renderer, bool obj, bool engB, int slot) {
28 color_t* palette;
29 color_t* variantPalette;
30 struct GBAVideoSoftwareRenderer* softwareRenderer;
31 uint16_t* vram;
32 if (!obj) {
33 if (!engB) {
34 palette = &renderer->extPaletteA[slot * 4096];
35 variantPalette = &renderer->variantPaletteA[slot * 4096];
36 softwareRenderer = &renderer->engA;
37 vram = renderer->d.vramABGExtPal[slot];
38 } else {
39 palette = &renderer->extPaletteB[slot * 4096];
40 variantPalette = &renderer->variantPaletteB[slot * 4096];
41 softwareRenderer = &renderer->engB;
42 vram = renderer->d.vramBBGExtPal[slot];
43 }
44 } else {
45 if (!engB) {
46 palette = renderer->objExtPaletteA;
47 variantPalette = renderer->variantPaletteA;
48 softwareRenderer = &renderer->engA;
49 vram = renderer->d.vramAOBJExtPal;
50 } else {
51 palette = renderer->objExtPaletteB;
52 variantPalette = renderer->variantPaletteB;
53 softwareRenderer = &renderer->engB;
54 vram = renderer->d.vramBOBJExtPal;
55 }
56 }
57 if (!vram) {
58 return false;
59 }
60 int i;
61 for (i = 0; i < 4096; ++i) {
62 uint16_t value = vram[i];
63#ifdef COLOR_16_BIT
64#ifdef COLOR_5_6_5
65 unsigned color = 0;
66 color |= (value & 0x001F) << 11;
67 color |= (value & 0x03E0) << 1;
68 color |= (value & 0x7C00) >> 10;
69#else
70 unsigned color = value;
71#endif
72#else
73 unsigned color = 0;
74 color |= (value << 3) & 0xF8;
75 color |= (value << 6) & 0xF800;
76 color |= (value << 9) & 0xF80000;
77 color |= (color >> 5) & 0x070707;
78#endif
79 palette[i] = color;
80 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
81 variantPalette[i] = _brighten(color, softwareRenderer->bldy);
82 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
83 variantPalette[i] = _darken(color, softwareRenderer->bldy);
84 }
85 }
86 if (obj) {
87 softwareRenderer->objExtPalette = palette;
88 softwareRenderer->objExtVariantPalette = variantPalette;
89 } else {
90 if (slot >= 2) {
91 if (GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot - 2].control)) {
92 softwareRenderer->bg[slot - 2].extPalette = palette;
93 softwareRenderer->bg[slot - 2].variantPalette = variantPalette;
94 }
95 } else if (slot < 2 && !GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot].control) ) {
96 softwareRenderer->bg[slot].extPalette = palette;
97 softwareRenderer->bg[slot].variantPalette = variantPalette;
98 }
99 softwareRenderer->bg[slot].extPalette = palette;
100 softwareRenderer->bg[slot].variantPalette = variantPalette;
101 }
102 return true;
103}
104
105static void _updateCharBase(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
106 struct GBAVideoSoftwareRenderer* eng;
107 if (!engB) {
108 eng = &softwareRenderer->engA;
109 } else {
110 eng = &softwareRenderer->engB;
111 }
112 int i;
113 uint32_t charBase = DSRegisterDISPCNTGetCharBase(softwareRenderer->dispcntA) << 16;
114 uint32_t screenBase = DSRegisterDISPCNTGetScreenBase(softwareRenderer->dispcntA) << 16;
115 for (i = 0; i < 4; ++i) {
116 if (!engB) {
117 uint32_t control = eng->bg[i].control;
118 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_BG0CNT + i * 2, control);
119 eng->bg[i].control = control;
120 }
121
122 eng->bg[i].charBase = GBARegisterBGCNTGetCharBase(eng->bg[i].control) << 14;
123
124 if (!engB) {
125 softwareRenderer->engA.bg[i].charBase += charBase;
126 softwareRenderer->engA.bg[i].screenBase &= ~0x70000;
127 softwareRenderer->engA.bg[i].screenBase |= screenBase;
128 }
129 }
130}
131
132
133void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
134 renderer->d.init = DSVideoSoftwareRendererInit;
135 renderer->d.reset = DSVideoSoftwareRendererReset;
136 renderer->d.deinit = DSVideoSoftwareRendererDeinit;
137 renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
138 renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
139 renderer->d.writeOAM = DSVideoSoftwareRendererWriteOAM;
140 renderer->d.invalidateExtPal = DSVideoSoftwareRendererInvalidateExtPal;
141 renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
142 renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
143 renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
144 renderer->d.putPixels = DSVideoSoftwareRendererPutPixels;
145
146 renderer->engA.d.cache = NULL;
147 GBAVideoSoftwareRendererCreate(&renderer->engA);
148 renderer->engB.d.cache = NULL;
149 GBAVideoSoftwareRendererCreate(&renderer->engB);
150}
151
152static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer) {
153 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
154 softwareRenderer->engA.d.palette = &renderer->palette[0];
155 softwareRenderer->engA.d.oam = &renderer->oam->oam[0];
156 softwareRenderer->engA.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
157 softwareRenderer->engA.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
158 softwareRenderer->engA.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
159 softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride;
160 softwareRenderer->engB.d.palette = &renderer->palette[512];
161 softwareRenderer->engB.d.oam = &renderer->oam->oam[1];
162 softwareRenderer->engB.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
163 softwareRenderer->engB.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
164 softwareRenderer->engB.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
165 softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride;
166
167 DSVideoSoftwareRendererReset(renderer);
168}
169
170static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer) {
171 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
172 softwareRenderer->engA.d.reset(&softwareRenderer->engA.d);
173 softwareRenderer->engB.d.reset(&softwareRenderer->engB.d);
174 softwareRenderer->powcnt = 0;
175 softwareRenderer->dispcntA = 0;
176 softwareRenderer->dispcntB = 0;
177}
178
179static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
180 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
181 softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
182 softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
183}
184
185static void DSVideoSoftwareRendererUpdateDISPCNT(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
186 uint32_t dispcnt;
187 struct GBAVideoSoftwareRenderer* eng;
188 if (!engB) {
189 dispcnt = softwareRenderer->dispcntA;
190 eng = &softwareRenderer->engA;
191 } else {
192 dispcnt = softwareRenderer->dispcntB;
193 eng = &softwareRenderer->engB;
194 }
195 uint16_t fakeDispcnt = dispcnt & 0xFF87;
196 if (!DSRegisterDISPCNTIsTileObjMapping(dispcnt)) {
197 eng->tileStride = 0x20;
198 } else {
199 eng->tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(dispcnt);
200 fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
201 }
202 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
203 eng->dispcnt |= dispcnt & 0xFFFF0000;
204 if (DSRegisterDISPCNTIsBgExtPalette(dispcnt)) {
205 color_t* extPalette;
206 if (!engB) {
207 extPalette = softwareRenderer->extPaletteA;
208 } else {
209 extPalette = softwareRenderer->extPaletteB;
210 }
211 int i;
212 for (i = 0; i < 4; ++i) {
213 int slot = i;
214 if (i < 2 && GBARegisterBGCNTIsExtPaletteSlot(eng->bg[i].control)) {
215 slot += 2;
216 }
217 if (eng->bg[i].extPalette != &extPalette[slot * 4096]) {
218 _regenerateExtPalette(softwareRenderer, false, engB, slot);
219 }
220 }
221 } else {
222 eng->bg[0].extPalette = NULL;
223 eng->bg[1].extPalette = NULL;
224 eng->bg[2].extPalette = NULL;
225 eng->bg[3].extPalette = NULL;
226 }
227 if (DSRegisterDISPCNTIsObjExtPalette(dispcnt)) {
228 if (!engB) {
229 if (softwareRenderer->engA.objExtPalette != softwareRenderer->objExtPaletteA) {
230 _regenerateExtPalette(softwareRenderer, true, engB, 0);
231 }
232 } else {
233 if (softwareRenderer->engB.objExtPalette != softwareRenderer->objExtPaletteB) {
234 _regenerateExtPalette(softwareRenderer, true, engB, 0);
235 }
236 }
237 } else {
238 if (!engB) {
239 softwareRenderer->engA.objExtPalette = NULL;
240 } else {
241 softwareRenderer->engB.objExtPalette = NULL;
242 }
243 }
244 if (!engB) {
245 eng->dispcnt = DSRegisterDISPCNTClear3D(eng->dispcnt);
246 eng->dispcnt |= DSRegisterDISPCNTIs3D(dispcnt);
247 _updateCharBase(softwareRenderer, engB);
248 }
249}
250
251static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
252 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
253 if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
254 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
255 } else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
256 softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
257 } else {
258 mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
259 }
260 switch (address) {
261 case DS9_REG_A_BG0CNT:
262 case DS9_REG_A_BG1CNT:
263 softwareRenderer->engA.bg[(address - DS9_REG_A_BG0CNT) >> 1].control = value;
264 // Fall through
265 case DS9_REG_A_BG2CNT:
266 case DS9_REG_A_BG3CNT:
267 _updateCharBase(softwareRenderer, false);
268 break;
269 case DS9_REG_B_BG0CNT:
270 case DS9_REG_B_BG1CNT:
271 softwareRenderer->engB.bg[(address - DS9_REG_B_BG0CNT) >> 1].control = value;
272 // Fall through
273 case DS9_REG_B_BG2CNT:
274 case DS9_REG_B_BG3CNT:
275 _updateCharBase(softwareRenderer, true);
276 break;
277 case DS9_REG_A_MASTER_BRIGHT:
278 softwareRenderer->engA.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
279 softwareRenderer->engA.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
280 if (softwareRenderer->engA.masterBrightY > 0x10) {
281 softwareRenderer->engA.masterBrightY = 0x10;
282 }
283 break;
284 case DS9_REG_B_MASTER_BRIGHT:
285 softwareRenderer->engB.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
286 softwareRenderer->engB.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
287 if (softwareRenderer->engB.masterBrightY > 0x10) {
288 softwareRenderer->engB.masterBrightY = 0x10;
289 }
290 break;
291 case DS9_REG_A_BLDCNT:
292 case DS9_REG_A_BLDY:
293 // TODO: Optimize
294 _regenerateExtPalette(softwareRenderer, false, false, 0);
295 _regenerateExtPalette(softwareRenderer, false, false, 1);
296 _regenerateExtPalette(softwareRenderer, false, false, 2);
297 _regenerateExtPalette(softwareRenderer, false, false, 3);
298 _regenerateExtPalette(softwareRenderer, true, false, 0);
299 break;
300 case DS9_REG_B_BLDCNT:
301 case DS9_REG_B_BLDY:
302 // TODO: Optimize
303 _regenerateExtPalette(softwareRenderer, false, true, 0);
304 _regenerateExtPalette(softwareRenderer, false, true, 1);
305 _regenerateExtPalette(softwareRenderer, false, true, 2);
306 _regenerateExtPalette(softwareRenderer, false, true, 3);
307 _regenerateExtPalette(softwareRenderer, true, true, 0);
308 break;
309 case DS9_REG_A_DISPCNT_LO:
310 softwareRenderer->dispcntA &= 0xFFFF0000;
311 softwareRenderer->dispcntA |= value;
312 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
313 break;
314 case DS9_REG_A_DISPCNT_HI:
315 softwareRenderer->dispcntA &= 0x0000FFFF;
316 softwareRenderer->dispcntA |= value << 16;
317 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
318 break;
319 case DS9_REG_B_DISPCNT_LO:
320 softwareRenderer->dispcntB &= 0xFFFF0000;
321 softwareRenderer->dispcntB |= value;
322 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
323 break;
324 case DS9_REG_B_DISPCNT_HI:
325 softwareRenderer->dispcntB &= 0x0000FFFF;
326 softwareRenderer->dispcntB |= value << 16;
327 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
328 break;
329 case DS9_REG_POWCNT1:
330 value &= 0x810F;
331 softwareRenderer->powcnt = value;
332 }
333 return value;
334}
335
336static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
337 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
338 if (address < 0x400) {
339 softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
340 } else {
341 softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
342 }
343}
344
345static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
346 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
347 if (oam < 0x200) {
348 softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
349 } else {
350 softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
351 }
352}
353
354static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
355 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
356 _regenerateExtPalette(softwareRenderer, obj, engB, slot);
357}
358
359static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* renderer, struct DSGX* gx, int y) {
360 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
361
362 int x;
363 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
364 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
365 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
366 row[x] = GBA_COLOR_WHITE;
367 }
368 return;
369 }
370
371 GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
372 int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
373
374 int w;
375 unsigned priority;
376 for (priority = 0; priority < 4; ++priority) {
377 softwareRenderer->end = 0;
378 for (w = 0; w < softwareRenderer->nWindows; ++w) {
379 softwareRenderer->start = softwareRenderer->end;
380 softwareRenderer->end = softwareRenderer->windows[w].endX;
381 softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
382 if (spriteLayers & (1 << priority)) {
383 GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
384 }
385 if (TEST_LAYER_ENABLED(0)) {
386 if (DSRegisterDISPCNTIs3D(softwareRenderer->dispcnt) && gx) {
387 color_t* scanline;
388 gx->renderer->getScanline(gx->renderer, y, &scanline);
389 uint32_t flags = (softwareRenderer->bg[0].priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
390 flags |= FLAG_TARGET_2 * softwareRenderer->bg[0].target2;
391 flags |= FLAG_TARGET_1 * (softwareRenderer->bg[0].target1 && softwareRenderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(softwareRenderer->currentWindow.packed));
392 int x;
393 for (x = softwareRenderer->start; x < softwareRenderer->end; ++x) {
394 if (scanline[x] & 0xF8000000) {
395 _compositeBlendNoObjwin(softwareRenderer, &softwareRenderer->row[x], (scanline[x] & 0x00FFFFFF) | flags, softwareRenderer->row[x]);
396 }
397 }
398 } else {
399 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
400 }
401 }
402 if (TEST_LAYER_ENABLED(1)) {
403 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
404 }
405 if (TEST_LAYER_ENABLED(2)) {
406 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
407 case 0:
408 case 1:
409 case 3:
410 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
411 break;
412 case 2:
413 case 4:
414 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
415 break;
416 case 5:
417 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[2].control)) {
418 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[2], y);
419 } else {
420 // TODO
421 }
422 break;
423 }
424 }
425 if (TEST_LAYER_ENABLED(3)) {
426 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
427 case 0:
428 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
429 break;
430 case 1:
431 case 2:
432 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
433 break;
434 case 3:
435 case 4:
436 case 5:
437 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[3].control)) {
438 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[3], y);
439 } else {
440 // TODO
441 }
442 break;
443 }
444 }
445 }
446 }
447 softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
448 softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
449 softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
450 softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
451
452 GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
453
454#ifdef COLOR_16_BIT
455#if defined(__ARM_NEON) && !defined(__APPLE__)
456 _to16Bit(row, softwareRenderer->row, softwareRenderer->masterEnd);
457#else
458 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
459 row[x] = softwareRenderer->row[x];
460 }
461#endif
462#else
463 switch (softwareRenderer->masterBright) {
464 case 0:
465 default:
466 memcpy(row, softwareRenderer->row, softwareRenderer->masterEnd * sizeof(*row));
467 break;
468 case 1:
469 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
470 row[x] = _brighten(softwareRenderer->row[x], softwareRenderer->masterBrightY);
471 }
472 break;
473 case 2:
474 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
475 row[x] = _darken(softwareRenderer->row[x], softwareRenderer->masterBrightY);
476 }
477 break;
478 }
479#endif
480}
481
482static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
483 memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
484 memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
485 color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
486
487 int x;
488 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
489 case 0:
490 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
491 row[x] = GBA_COLOR_WHITE;
492 }
493 return;
494 case 1:
495 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
496 return;
497 case 2: {
498 uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
499 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
500 color_t color;
501 LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
502#ifndef COLOR_16_BIT
503 unsigned color32 = 0;
504 color32 |= (color << 9) & 0xF80000;
505 color32 |= (color << 3) & 0xF8;
506 color32 |= (color << 6) & 0xF800;
507 color32 |= (color32 >> 5) & 0x070707;
508 color = color32;
509#elif COLOR_5_6_5
510 uint16_t color16 = 0;
511 color16 |= (color & 0x001F) << 11;
512 color16 |= (color & 0x03E0) << 1;
513 color16 |= (color & 0x7C00) >> 10;
514 color = color16;
515#endif
516 softwareRenderer->row[x] = color;
517 }
518 break;
519 }
520 case 3:
521 break;
522 }
523
524#ifdef COLOR_16_BIT
525#if defined(__ARM_NEON) && !defined(__APPLE__)
526 _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
527#else
528 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
529 row[x] = softwareRenderer->row[x];
530 }
531#endif
532#else
533 memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
534#endif
535}
536
537static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
538 memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
539 memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
540 color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
541
542 int x;
543 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
544 case 0:
545 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
546 row[x] = GBA_COLOR_WHITE;
547 }
548 return;
549 case 1:
550 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engB.d, NULL, y);
551 return;
552 }
553
554#ifdef COLOR_16_BIT
555#if defined(__ARM_NEON) && !defined(__APPLE__)
556 _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
557#else
558 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
559 row[x] = softwareRenderer->row[x];
560 }
561#endif
562#else
563 memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
564#endif
565}
566
567static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
568 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
569 if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
570 softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
571 softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
572 } else {
573 softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
574 softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
575 }
576
577 _drawScanlineA(softwareRenderer, y);
578 _drawScanlineB(softwareRenderer, y);
579}
580
581static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
582 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
583 softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
584 softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
585}
586
587static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
588 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
589#ifdef COLOR_16_BIT
590#error Not yet supported
591#else
592 *stride = softwareRenderer->outputBufferStride;
593 *pixels = softwareRenderer->outputBuffer;
594#endif
595}
596
597static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
598}
599
600#define EXT_0_COORD_OVERFLOW \
601 localX = x & (sizeAdjusted - 1); \
602 localY = y & (sizeAdjusted - 1); \
603
604#define EXT_0_COORD_NO_OVERFLOW \
605 if ((x | y) & ~(sizeAdjusted - 1)) { \
606 continue; \
607 } \
608 localX = x; \
609 localY = y;
610
611#define EXT_0_NO_MOSAIC(COORD) \
612 COORD \
613 uint32_t screenBase = background->screenBase + (localX >> 10) + (((localY >> 6) & 0xFE0) << background->size); \
614 uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
615 if (UNLIKELY(!screenBlock)) { \
616 continue; \
617 } \
618 LOAD_16(mapData, screenBase & (VRAM_BLOCK_MASK - 1), screenBlock); \
619 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
620 palette = &mainPalette[paletteData]; \
621 uint32_t charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8); \
622 uint16_t* vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
623 if (UNLIKELY(!vram)) { \
624 continue; \
625 } \
626 pixelData = ((uint8_t*) vram)[charBase & VRAM_BLOCK_MASK];
627
628#define EXT_0_MOSAIC(COORD) \
629 if (!mosaicWait) { \
630 EXT_0_NO_MOSAIC(COORD) \
631 mosaicWait = mosaicH; \
632 } else { \
633 --mosaicWait; \
634 }
635
636#define EXT_0_LOOP(MOSAIC, COORD, BLEND, OBJWIN) \
637 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
638 x += background->dx; \
639 y += background->dy; \
640 \
641 uint32_t current = *pixel; \
642 MOSAIC(COORD) \
643 if (pixelData) { \
644 COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
645 } \
646 }
647
648#define DRAW_BACKGROUND_EXT_0(BLEND, OBJWIN) \
649 if (background->overflow) { \
650 if (mosaicH > 1) { \
651 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
652 } else { \
653 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
654 } \
655 } else { \
656 if (mosaicH > 1) { \
657 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
658 } else { \
659 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
660 } \
661 }
662
663void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
664 int sizeAdjusted = 0x8000 << background->size;
665
666 BACKGROUND_BITMAP_INIT;
667
668 color_t* mainPalette = background->extPalette;
669 if (variant) {
670 mainPalette = background->variantPalette;
671 }
672 if (!mainPalette) {
673 return;
674 }
675 int paletteData;
676
677 uint16_t mapData;
678 uint8_t pixelData = 0;
679
680 int outX;
681 uint32_t* pixel;
682
683 if (!objwinSlowPath) {
684 if (!(flags & FLAG_TARGET_2)) {
685 DRAW_BACKGROUND_EXT_0(NoBlend, NO_OBJWIN);
686 } else {
687 DRAW_BACKGROUND_EXT_0(Blend, NO_OBJWIN);
688 }
689 } else {
690 if (!(flags & FLAG_TARGET_2)) {
691 DRAW_BACKGROUND_EXT_0(NoBlend, OBJWIN);
692 } else {
693 DRAW_BACKGROUND_EXT_0(Blend, OBJWIN);
694 }
695 }
696}