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