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