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