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 _updateCharBase(softwareRenderer, engB);
242 }
243}
244
245static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
246 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
247 if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
248 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
249 } else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
250 softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
251 } else {
252 mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
253 }
254 switch (address) {
255 case DS9_REG_A_BG0CNT:
256 case DS9_REG_A_BG1CNT:
257 softwareRenderer->engA.bg[(address - DS9_REG_A_BG0CNT) >> 1].control = value;
258 // Fall through
259 case DS9_REG_A_BG2CNT:
260 case DS9_REG_A_BG3CNT:
261 _updateCharBase(softwareRenderer, false);
262 break;
263 case DS9_REG_B_BG0CNT:
264 case DS9_REG_B_BG1CNT:
265 softwareRenderer->engB.bg[(address - DS9_REG_B_BG0CNT) >> 1].control = value;
266 // Fall through
267 case DS9_REG_B_BG2CNT:
268 case DS9_REG_B_BG3CNT:
269 _updateCharBase(softwareRenderer, true);
270 break;
271 case DS9_REG_A_DISPCNT_LO:
272 softwareRenderer->dispcntA &= 0xFFFF0000;
273 softwareRenderer->dispcntA |= value;
274 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
275 break;
276 case DS9_REG_A_DISPCNT_HI:
277 softwareRenderer->dispcntA &= 0x0000FFFF;
278 softwareRenderer->dispcntA |= value << 16;
279 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
280 break;
281 case DS9_REG_B_DISPCNT_LO:
282 softwareRenderer->dispcntB &= 0xFFFF0000;
283 softwareRenderer->dispcntB |= value;
284 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
285 break;
286 case DS9_REG_B_DISPCNT_HI:
287 softwareRenderer->dispcntB &= 0x0000FFFF;
288 softwareRenderer->dispcntB |= value << 16;
289 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
290 break;
291 case DS9_REG_POWCNT1:
292 value &= 0x810F;
293 softwareRenderer->powcnt = value;
294 }
295 return value;
296}
297
298static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
299 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
300 if (address < 0x400) {
301 softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
302 } else {
303 softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
304 }
305}
306
307static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
308 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
309 if (oam < 0x200) {
310 softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
311 } else {
312 softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
313 }
314}
315
316static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
317 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
318 _regenerateExtPalette(softwareRenderer, obj, engB, slot);
319}
320
321static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* renderer, int y) {
322 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
323
324 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
325 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
326 int x;
327 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
328 row[x] = GBA_COLOR_WHITE;
329 }
330 return;
331 }
332
333 GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
334 int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
335
336 int w;
337 unsigned priority;
338 for (priority = 0; priority < 4; ++priority) {
339 softwareRenderer->end = 0;
340 for (w = 0; w < softwareRenderer->nWindows; ++w) {
341 softwareRenderer->start = softwareRenderer->end;
342 softwareRenderer->end = softwareRenderer->windows[w].endX;
343 softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
344 if (spriteLayers & (1 << priority)) {
345 GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
346 }
347 if (TEST_LAYER_ENABLED(0)) {
348 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
349 }
350 if (TEST_LAYER_ENABLED(1)) {
351 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
352 }
353 if (TEST_LAYER_ENABLED(2)) {
354 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
355 case 0:
356 case 1:
357 case 3:
358 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
359 break;
360 case 2:
361 case 4:
362 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
363 break;
364 case 5:
365 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[2], y);
366 break;
367 }
368 }
369 if (TEST_LAYER_ENABLED(3)) {
370 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
371 case 0:
372 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
373 break;
374 case 1:
375 case 2:
376 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
377 break;
378 case 3:
379 case 4:
380 case 5:
381 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[3], y);
382 break;
383 }
384 }
385 }
386 }
387 softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
388 softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
389 softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
390 softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
391
392 GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
393
394#ifdef COLOR_16_BIT
395#if defined(__ARM_NEON) && !defined(__APPLE__)
396 _to16Bit(row, softwareRenderer->row, softwareRenderer->masterEnd);
397#else
398 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
399 row[x] = softwareRenderer->row[x];
400 }
401#endif
402#else
403 memcpy(row, softwareRenderer->row, softwareRenderer->masterEnd * sizeof(*row));
404#endif
405}
406
407static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
408 memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
409 memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
410 color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
411
412 int x;
413 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
414 case 0:
415 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
416 row[x] = GBA_COLOR_WHITE;
417 }
418 return;
419 case 1:
420 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, y);
421 return;
422 case 2: {
423 uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
424 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
425 color_t color;
426 LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
427#ifndef COLOR_16_BIT
428 unsigned color32 = 0;
429 color32 |= (color << 9) & 0xF80000;
430 color32 |= (color << 3) & 0xF8;
431 color32 |= (color << 6) & 0xF800;
432 color32 |= (color32 >> 5) & 0x070707;
433 color = color32;
434#elif COLOR_5_6_5
435 uint16_t color16 = 0;
436 color16 |= (color & 0x001F) << 11;
437 color16 |= (color & 0x03E0) << 1;
438 color16 |= (color & 0x7C00) >> 10;
439 color = color16;
440#endif
441 softwareRenderer->row[x] = color;
442 }
443 break;
444 }
445 case 3:
446 break;
447 }
448
449#ifdef COLOR_16_BIT
450#if defined(__ARM_NEON) && !defined(__APPLE__)
451 _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
452#else
453 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
454 row[x] = softwareRenderer->row[x];
455 }
456#endif
457#else
458 memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
459#endif
460}
461
462static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
463 memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
464 memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
465 color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
466
467 int x;
468 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
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->engB.d, y);
476 return;
477 }
478
479#ifdef COLOR_16_BIT
480#if defined(__ARM_NEON) && !defined(__APPLE__)
481 _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
482#else
483 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
484 row[x] = softwareRenderer->row[x];
485 }
486#endif
487#else
488 memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
489#endif
490}
491
492static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
493 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
494 if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
495 softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
496 softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
497 } else {
498 softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
499 softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
500 }
501
502 _drawScanlineA(softwareRenderer, y);
503 _drawScanlineB(softwareRenderer, y);
504}
505
506static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
507 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
508 softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
509 softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
510}
511
512static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
513 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
514#ifdef COLOR_16_BIT
515#error Not yet supported
516#else
517 *stride = softwareRenderer->outputBufferStride;
518 *pixels = softwareRenderer->outputBuffer;
519#endif
520}
521
522static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
523}
524
525#define EXT_0_COORD_OVERFLOW \
526 localX = x & (sizeAdjusted - 1); \
527 localY = y & (sizeAdjusted - 1); \
528
529#define EXT_0_COORD_NO_OVERFLOW \
530 if ((x | y) & ~(sizeAdjusted - 1)) { \
531 continue; \
532 } \
533 localX = x; \
534 localY = y;
535
536#define EXT_0_NO_MOSAIC(COORD) \
537 COORD \
538 uint32_t screenBase = background->screenBase + (localX >> 10) + (((localY >> 6) & 0xFE0) << background->size); \
539 uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
540 if (UNLIKELY(!screenBlock)) { \
541 continue; \
542 } \
543 LOAD_16(mapData, screenBase & (VRAM_BLOCK_MASK - 1), screenBlock); \
544 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
545 palette = &mainPalette[paletteData]; \
546 uint32_t charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8); \
547 uint16_t* vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
548 if (UNLIKELY(!vram)) { \
549 continue; \
550 } \
551 pixelData = ((uint8_t*) vram)[charBase & VRAM_BLOCK_MASK];
552
553#define EXT_0_MOSAIC(COORD) \
554 if (!mosaicWait) { \
555 EXT_0_NO_MOSAIC(COORD) \
556 mosaicWait = mosaicH; \
557 } else { \
558 --mosaicWait; \
559 }
560
561#define EXT_0_LOOP(MOSAIC, COORD, BLEND, OBJWIN) \
562 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
563 x += background->dx; \
564 y += background->dy; \
565 \
566 uint32_t current = *pixel; \
567 MOSAIC(COORD) \
568 if (pixelData) { \
569 COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
570 } \
571 }
572
573#define DRAW_BACKGROUND_EXT_0(BLEND, OBJWIN) \
574 if (background->overflow) { \
575 if (mosaicH > 1) { \
576 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
577 } else { \
578 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, BLEND, OBJWIN); \
579 } \
580 } else { \
581 if (mosaicH > 1) { \
582 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
583 } else { \
584 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, BLEND, OBJWIN); \
585 } \
586 }
587
588void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
589 int sizeAdjusted = 0x8000 << background->size;
590
591 BACKGROUND_BITMAP_INIT;
592
593 color_t* mainPalette = background->extPalette;
594 int paletteData;
595
596 uint16_t mapData;
597 uint8_t pixelData = 0;
598
599 int outX;
600 uint32_t* pixel;
601
602 if (!objwinSlowPath) {
603 if (!(flags & FLAG_TARGET_2)) {
604 DRAW_BACKGROUND_EXT_0(NoBlend, NO_OBJWIN);
605 } else {
606 DRAW_BACKGROUND_EXT_0(Blend, NO_OBJWIN);
607 }
608 } else {
609 if (!(flags & FLAG_TARGET_2)) {
610 DRAW_BACKGROUND_EXT_0(NoBlend, OBJWIN);
611 } else {
612 DRAW_BACKGROUND_EXT_0(Blend, OBJWIN);
613 }
614 }
615}