src/gba/renderers/video-software.c (view raw)
1#include "video-software.h"
2
3#include "gba.h"
4#include "gba-io.h"
5
6static const int _objSizes[32] = {
7 8, 8,
8 16, 16,
9 32, 32,
10 64, 64,
11 16, 8,
12 32, 8,
13 32, 16,
14 64, 32,
15 8, 16,
16 8, 32,
17 16, 32,
18 32, 64,
19 0, 0,
20 0, 0,
21 0, 0,
22 0, 0
23};
24
25DECL_BITFIELD(GBARegisterBLDCNT, uint16_t);
26DECL_BIT(GBARegisterBLDCNT, Target1Bg0, 0);
27DECL_BIT(GBARegisterBLDCNT, Target1Bg1, 1);
28DECL_BIT(GBARegisterBLDCNT, Target1Bg2, 2);
29DECL_BIT(GBARegisterBLDCNT, Target1Bg3, 3);
30DECL_BIT(GBARegisterBLDCNT, Target1Obj, 4);
31DECL_BIT(GBARegisterBLDCNT, Target1Bd, 5);
32DECL_BITS(GBARegisterBLDCNT, Effect, 6, 2);
33DECL_BIT(GBARegisterBLDCNT, Target2Bg0, 8);
34DECL_BIT(GBARegisterBLDCNT, Target2Bg1, 9);
35DECL_BIT(GBARegisterBLDCNT, Target2Bg2, 10);
36DECL_BIT(GBARegisterBLDCNT, Target2Bg3, 11);
37DECL_BIT(GBARegisterBLDCNT, Target2Obj, 12);
38DECL_BIT(GBARegisterBLDCNT, Target2Bd, 13);
39
40static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
41static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
42static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
43static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
44static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
45static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
46static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
47static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels);
48static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels);
49
50static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
51static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
52static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
53static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
54static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
55static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
56static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
57static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
58static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
59static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
60static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
61
62static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
63static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
64static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
65static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
66static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
67static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
68static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer);
69static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
70static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority);
71
72static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
73static inline unsigned _brighten(unsigned color, int y);
74static inline unsigned _darken(unsigned color, int y);
75static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB);
76
77void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
78 renderer->d.init = GBAVideoSoftwareRendererInit;
79 renderer->d.reset = GBAVideoSoftwareRendererInit;
80 renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
81 renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
82 renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
83 renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
84 renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
85 renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
86 renderer->d.getPixels = GBAVideoSoftwareRendererGetPixels;
87 renderer->d.putPixels = GBAVideoSoftwareRendererPutPixels;
88}
89
90static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
91 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
92 int i;
93
94 softwareRenderer->dispcnt = 0x0080;
95
96 softwareRenderer->target1Obj = 0;
97 softwareRenderer->target1Bd = 0;
98 softwareRenderer->target2Obj = 0;
99 softwareRenderer->target2Bd = 0;
100 softwareRenderer->blendEffect = BLEND_NONE;
101 memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
102 memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
103
104 softwareRenderer->blda = 0;
105 softwareRenderer->bldb = 0;
106 softwareRenderer->bldy = 0;
107
108 softwareRenderer->winN[0] = (struct WindowN) { .control = { .priority = 0 } };
109 softwareRenderer->winN[1] = (struct WindowN) { .control = { .priority = 1 } };
110 softwareRenderer->objwin = (struct WindowControl) { .priority = 2 };
111 softwareRenderer->winout = (struct WindowControl) { .priority = 3 };
112 softwareRenderer->oamMax = 0;
113
114 softwareRenderer->mosaic.packed = 0;
115
116 for (i = 0; i < 4; ++i) {
117 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
118 bg->index = i;
119 bg->enabled = 0;
120 bg->priority = 0;
121 bg->charBase = 0;
122 bg->mosaic = 0;
123 bg->multipalette = 0;
124 bg->screenBase = 0;
125 bg->overflow = 0;
126 bg->size = 0;
127 bg->target1 = 0;
128 bg->target2 = 0;
129 bg->x = 0;
130 bg->y = 0;
131 bg->refx = 0;
132 bg->refy = 0;
133 bg->dx = 256;
134 bg->dmx = 0;
135 bg->dy = 0;
136 bg->dmy = 256;
137 bg->sx = 0;
138 bg->sy = 0;
139 }
140}
141
142static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
143 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
144 UNUSED(softwareRenderer);
145}
146
147static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
148 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
149 switch (address) {
150 case REG_DISPCNT:
151 softwareRenderer->dispcnt = value;
152 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
153 break;
154 case REG_BG0CNT:
155 value &= 0xFFCF;
156 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
157 break;
158 case REG_BG1CNT:
159 value &= 0xFFCF;
160 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
161 break;
162 case REG_BG2CNT:
163 value &= 0xFFCF;
164 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
165 break;
166 case REG_BG3CNT:
167 value &= 0xFFCF;
168 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
169 break;
170 case REG_BG0HOFS:
171 value &= 0x01FF;
172 softwareRenderer->bg[0].x = value;
173 break;
174 case REG_BG0VOFS:
175 value &= 0x01FF;
176 softwareRenderer->bg[0].y = value;
177 break;
178 case REG_BG1HOFS:
179 value &= 0x01FF;
180 softwareRenderer->bg[1].x = value;
181 break;
182 case REG_BG1VOFS:
183 value &= 0x01FF;
184 softwareRenderer->bg[1].y = value;
185 break;
186 case REG_BG2HOFS:
187 value &= 0x01FF;
188 softwareRenderer->bg[2].x = value;
189 break;
190 case REG_BG2VOFS:
191 value &= 0x01FF;
192 softwareRenderer->bg[2].y = value;
193 break;
194 case REG_BG3HOFS:
195 value &= 0x01FF;
196 softwareRenderer->bg[3].x = value;
197 break;
198 case REG_BG3VOFS:
199 value &= 0x01FF;
200 softwareRenderer->bg[3].y = value;
201 break;
202 case REG_BG2PA:
203 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
204 break;
205 case REG_BG2PB:
206 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
207 break;
208 case REG_BG2PC:
209 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
210 break;
211 case REG_BG2PD:
212 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
213 break;
214 case REG_BG2X_LO:
215 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
216 break;
217 case REG_BG2X_HI:
218 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
219 break;
220 case REG_BG2Y_LO:
221 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
222 break;
223 case REG_BG2Y_HI:
224 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
225 break;
226 case REG_BG3PA:
227 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
228 break;
229 case REG_BG3PB:
230 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
231 break;
232 case REG_BG3PC:
233 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
234 break;
235 case REG_BG3PD:
236 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
237 break;
238 case REG_BG3X_LO:
239 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
240 break;
241 case REG_BG3X_HI:
242 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
243 break;
244 case REG_BG3Y_LO:
245 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
246 break;
247 case REG_BG3Y_HI:
248 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
249 break;
250 case REG_BLDCNT:
251 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
252 break;
253 case REG_BLDALPHA:
254 softwareRenderer->blda = value & 0x1F;
255 if (softwareRenderer->blda > 0x10) {
256 softwareRenderer->blda = 0x10;
257 }
258 softwareRenderer->bldb = (value >> 8) & 0x1F;
259 if (softwareRenderer->bldb > 0x10) {
260 softwareRenderer->bldb = 0x10;
261 }
262 break;
263 case REG_BLDY:
264 softwareRenderer->bldy = value & 0x1F;
265 if (softwareRenderer->bldy > 0x10) {
266 softwareRenderer->bldy = 0x10;
267 }
268 _updatePalettes(softwareRenderer);
269 break;
270 case REG_WIN0H:
271 softwareRenderer->winN[0].h.end = value;
272 softwareRenderer->winN[0].h.start = value >> 8;
273 if (softwareRenderer->winN[0].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end) {
274 softwareRenderer->winN[0].h.start = 0;
275 }
276 if (softwareRenderer->winN[0].h.start > softwareRenderer->winN[0].h.end || softwareRenderer->winN[0].h.end > VIDEO_HORIZONTAL_PIXELS) {
277 softwareRenderer->winN[0].h.end = VIDEO_HORIZONTAL_PIXELS;
278 }
279 break;
280 case REG_WIN1H:
281 softwareRenderer->winN[1].h.end = value;
282 softwareRenderer->winN[1].h.start = value >> 8;
283 if (softwareRenderer->winN[1].h.start > VIDEO_HORIZONTAL_PIXELS && softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end) {
284 softwareRenderer->winN[1].h.start = 0;
285 }
286 if (softwareRenderer->winN[1].h.start > softwareRenderer->winN[1].h.end || softwareRenderer->winN[1].h.end > VIDEO_HORIZONTAL_PIXELS) {
287 softwareRenderer->winN[1].h.end = VIDEO_HORIZONTAL_PIXELS;
288 }
289 break;
290 case REG_WIN0V:
291 softwareRenderer->winN[0].v.end = value;
292 softwareRenderer->winN[0].v.start = value >> 8;
293 if (softwareRenderer->winN[0].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end) {
294 softwareRenderer->winN[0].v.start = 0;
295 }
296 if (softwareRenderer->winN[0].v.start > softwareRenderer->winN[0].v.end || softwareRenderer->winN[0].v.end > VIDEO_HORIZONTAL_PIXELS) {
297 softwareRenderer->winN[0].v.end = VIDEO_VERTICAL_PIXELS;
298 }
299 break;
300 case REG_WIN1V:
301 softwareRenderer->winN[1].v.end = value;
302 softwareRenderer->winN[1].v.start = value >> 8;
303 if (softwareRenderer->winN[1].v.start > VIDEO_VERTICAL_PIXELS && softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end) {
304 softwareRenderer->winN[1].v.start = 0;
305 }
306 if (softwareRenderer->winN[1].v.start > softwareRenderer->winN[1].v.end || softwareRenderer->winN[1].v.end > VIDEO_HORIZONTAL_PIXELS) {
307 softwareRenderer->winN[1].v.end = VIDEO_VERTICAL_PIXELS;
308 }
309 break;
310 case REG_WININ:
311 softwareRenderer->winN[0].control.packed = value;
312 softwareRenderer->winN[1].control.packed = value >> 8;
313 break;
314 case REG_WINOUT:
315 softwareRenderer->winout.packed = value;
316 softwareRenderer->objwin.packed = value >> 8;
317 break;
318 case REG_MOSAIC:
319 softwareRenderer->mosaic.packed = value;
320 break;
321 case REG_GREENSWP:
322 GBALog(0, GBA_LOG_STUB, "Stub video register write: 0x%03X", address);
323 break;
324 default:
325 GBALog(0, GBA_LOG_GAME_ERROR, "Invalid video register: 0x%03X", address);
326 }
327 return value;
328}
329
330static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
331 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
332 softwareRenderer->oamDirty = 1;
333 UNUSED(oam);
334}
335
336static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
337 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
338#ifdef COLOR_16_BIT
339#ifdef COLOR_5_6_5
340 unsigned color = 0;
341 color |= (value & 0x001F) << 11;
342 color |= (value & 0x03E0) << 1;
343 color |= (value & 0x7C00) >> 10;
344#else
345 unsigned color = value;
346#endif
347#else
348 unsigned color = 0;
349 color |= (value << 3) & 0xF8;
350 color |= (value << 6) & 0xF800;
351 color |= (value << 9) & 0xF80000;
352#endif
353 softwareRenderer->normalPalette[address >> 1] = color;
354 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
355 softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
356 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
357 softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
358 }
359}
360
361static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win) {
362 int activeWindow;
363 int startX = 0;
364 if (win->h.end > 0) {
365 for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) {
366 if (win->h.start < softwareRenderer->windows[activeWindow].endX) {
367 // Insert a window before the end of the active window
368 struct Window oldWindow = softwareRenderer->windows[activeWindow];
369 if (win->h.start > startX) {
370 // And after the start of the active window
371 int nextWindow = softwareRenderer->nWindows;
372 ++softwareRenderer->nWindows;
373 for (; nextWindow > activeWindow; --nextWindow) {
374 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
375 }
376 softwareRenderer->windows[activeWindow].endX = win->h.start;
377 ++activeWindow;
378 }
379 softwareRenderer->windows[activeWindow].control = win->control;
380 softwareRenderer->windows[activeWindow].endX = win->h.end;
381 if (win->h.end >= oldWindow.endX) {
382 // Trim off extra windows we've overwritten
383 for (++activeWindow; win->h.end >= softwareRenderer->windows[activeWindow].endX && softwareRenderer->nWindows > activeWindow; ++activeWindow) {
384 softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1];
385 --softwareRenderer->nWindows;
386 }
387 } else {
388 ++activeWindow;
389 int nextWindow = softwareRenderer->nWindows;
390 ++softwareRenderer->nWindows;
391 for (; nextWindow > activeWindow; --nextWindow) {
392 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1];
393 }
394 softwareRenderer->windows[activeWindow] = oldWindow;
395 }
396 break;
397 }
398 startX = softwareRenderer->windows[activeWindow].endX;
399 }
400 }
401}
402
403static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) {
404 int i;
405 int oamMax = 0;
406 for (i = 0; i < 128; ++i) {
407 struct GBAObj obj;
408 LOAD_16(obj.a, 0, &renderer->d.oam->obj[i].a);
409 LOAD_16(obj.b, 0, &renderer->d.oam->obj[i].b);
410 LOAD_16(obj.c, 0, &renderer->d.oam->obj[i].c);
411 if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) {
412 int height = _objSizes[GBAObjAttributesAGetShape(obj.a) * 8 + GBAObjAttributesBGetSize(obj.b) * 2 + 1];
413 if (GBAObjAttributesAIsTransformed(obj.a)) {
414 height <<= GBAObjAttributesAGetDoubleSize(obj.a);
415 }
416 if (GBAObjAttributesAGetY(obj.a) < VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) {
417 renderer->sprites[oamMax].y = GBAObjAttributesAGetY(obj.a);
418 renderer->sprites[oamMax].endY = GBAObjAttributesAGetY(obj.a) + height;
419 renderer->sprites[oamMax].obj = obj;
420 ++oamMax;
421 }
422 }
423 }
424 renderer->oamMax = oamMax;
425 renderer->oamDirty = 0;
426}
427
428
429static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
430 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
431
432 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
433 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
434 int x;
435 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
436 row[x] = GBA_COLOR_WHITE;
437 }
438 return;
439 }
440
441 int x;
442 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; x += 4) {
443 softwareRenderer->spriteLayer[x] = FLAG_UNWRITTEN;
444 softwareRenderer->spriteLayer[x + 1] = FLAG_UNWRITTEN;
445 softwareRenderer->spriteLayer[x + 2] = FLAG_UNWRITTEN;
446 softwareRenderer->spriteLayer[x + 3] = FLAG_UNWRITTEN;
447 }
448
449 softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS;
450 softwareRenderer->nWindows = 1;
451 if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) || GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) {
452 softwareRenderer->windows[0].control = softwareRenderer->winout;
453 if (GBARegisterDISPCNTIsWin1Enable(softwareRenderer->dispcnt) && y < softwareRenderer->winN[1].v.end && y >= softwareRenderer->winN[1].v.start) {
454 _breakWindow(softwareRenderer, &softwareRenderer->winN[1]);
455 }
456 if (GBARegisterDISPCNTIsWin0Enable(softwareRenderer->dispcnt) && y < softwareRenderer->winN[0].v.end && y >= softwareRenderer->winN[0].v.start) {
457 _breakWindow(softwareRenderer, &softwareRenderer->winN[0]);
458 }
459 } else {
460 softwareRenderer->windows[0].control.packed = 0xFF;
461 }
462
463 int w;
464 x = 0;
465 for (w = 0; w < softwareRenderer->nWindows; ++w) {
466 // TOOD: handle objwin on backdrop
467 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
468 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
469 backdrop |= softwareRenderer->normalPalette[0];
470 } else {
471 backdrop |= softwareRenderer->variantPalette[0];
472 }
473 int end = softwareRenderer->windows[w].endX;
474 for (; x < end; ++x) {
475 softwareRenderer->row[x] = backdrop;
476 }
477 }
478
479 _drawScanline(softwareRenderer, y);
480
481 if (softwareRenderer->target2Bd) {
482 x = 0;
483 for (w = 0; w < softwareRenderer->nWindows; ++w) {
484 uint32_t backdrop = FLAG_UNWRITTEN;
485 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) {
486 backdrop |= softwareRenderer->normalPalette[0];
487 } else {
488 backdrop |= softwareRenderer->variantPalette[0];
489 }
490 int end = softwareRenderer->windows[w].endX;
491 for (; x < end; ++x) {
492 uint32_t color = softwareRenderer->row[x];
493 if (color & FLAG_TARGET_1) {
494 softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
495 }
496 }
497 }
498 }
499
500#ifdef COLOR_16_BIT
501#ifdef __arm__
502 _to16Bit(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS);
503#else
504 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
505 row[x] = softwareRenderer->row[x];
506 }
507#endif
508#else
509 memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
510#endif
511}
512
513static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
514 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
515
516 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
517 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
518 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
519 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
520}
521
522static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
523 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
524
525 *stride = softwareRenderer->outputBufferStride;
526 *pixels = softwareRenderer->outputBuffer;
527}
528
529static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
530 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
531
532 uint32_t* colorPixels = pixels;
533 unsigned i;
534 for (i = 0; i < VIDEO_VERTICAL_PIXELS; ++i) {
535 memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
536 }
537}
538
539static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
540 renderer->bg[0].enabled = GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt);
541 renderer->bg[1].enabled = GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt);
542 renderer->bg[2].enabled = GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt);
543 renderer->bg[3].enabled = GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt);
544}
545
546static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
547 UNUSED(renderer);
548 bg->priority = GBARegisterBGCNTGetPriority(value);
549 bg->charBase = GBARegisterBGCNTGetCharBase(value) << 14;
550 bg->mosaic = GBARegisterBGCNTGetMosaic(value);
551 bg->multipalette = GBARegisterBGCNTGet256Color(value);
552 bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 11;
553 bg->overflow = GBARegisterBGCNTGetOverflow(value);
554 bg->size = GBARegisterBGCNTGetSize(value);
555}
556
557static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
558 bg->dx = value;
559}
560
561static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
562 bg->dmx = value;
563}
564
565static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
566 bg->dy = value;
567}
568
569static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
570 bg->dmy = value;
571}
572
573static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
574 bg->refx = (bg->refx & 0xFFFF0000) | value;
575 bg->sx = bg->refx;
576}
577
578static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
579 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
580 bg->refx <<= 4;
581 bg->refx >>= 4;
582 bg->sx = bg->refx;
583}
584
585static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
586 bg->refy = (bg->refy & 0xFFFF0000) | value;
587 bg->sy = bg->refy;
588}
589
590static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
591 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
592 bg->refy <<= 4;
593 bg->refy >>= 4;
594 bg->sy = bg->refy;
595}
596
597static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
598 enum BlendEffect oldEffect = renderer->blendEffect;
599
600 renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
601 renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
602 renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
603 renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
604 renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
605 renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
606 renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
607 renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
608
609 renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
610 renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
611 renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
612 renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
613 renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
614
615 renderer->anyTarget2 = value & 0x3F00;
616
617 if (oldEffect != renderer->blendEffect) {
618 _updatePalettes(renderer);
619 }
620}
621
622#define TEST_LAYER_ENABLED(X) \
623 (renderer->bg[X].enabled && \
624 (GBAWindowControlIsBg ## X ## Enable(renderer->currentWindow.packed) || \
625 (GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (renderer->objwin.packed))) && \
626 renderer->bg[X].priority == priority)
627
628static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
629 int w;
630 renderer->end = 0;
631 int spriteLayers = 0;
632 if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt)) {
633 if (renderer->oamDirty) {
634 _cleanOAM(renderer);
635 }
636 int mosaicV = renderer->mosaic.objV + 1;
637 int mosaicY = y - (y % mosaicV);
638 for (w = 0; w < renderer->nWindows; ++w) {
639 renderer->start = renderer->end;
640 renderer->end = renderer->windows[w].endX;
641 renderer->currentWindow = renderer->windows[w].control;
642 if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed)) {
643 continue;
644 }
645 int i;
646 int drawn;
647 for (i = 0; i < renderer->oamMax; ++i) {
648 int localY = y;
649 struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
650 if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
651 localY = mosaicY;
652 }
653 if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
654 continue;
655 }
656 drawn = _preprocessSprite(renderer, &sprite->obj, localY);
657 spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c);
658 }
659 }
660 }
661
662 int priority;
663 for (priority = 0; priority < 4; ++priority) {
664 if (spriteLayers & (1 << priority)) {
665 _postprocessSprite(renderer, priority);
666 }
667 renderer->end = 0;
668 for (w = 0; w < renderer->nWindows; ++w) {
669 renderer->start = renderer->end;
670 renderer->end = renderer->windows[w].endX;
671 renderer->currentWindow = renderer->windows[w].control;
672 if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
673 _drawBackgroundMode0(renderer, &renderer->bg[0], y);
674 }
675 if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(renderer->dispcnt) < 2) {
676 _drawBackgroundMode0(renderer, &renderer->bg[1], y);
677 }
678 if (TEST_LAYER_ENABLED(2)) {
679 switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
680 case 0:
681 _drawBackgroundMode0(renderer, &renderer->bg[2], y);
682 break;
683 case 1:
684 case 2:
685 _drawBackgroundMode2(renderer, &renderer->bg[2], y);
686 break;
687 case 3:
688 _drawBackgroundMode3(renderer, &renderer->bg[2], y);
689 break;
690 case 4:
691 _drawBackgroundMode4(renderer, &renderer->bg[2], y);
692 break;
693 case 5:
694 _drawBackgroundMode5(renderer, &renderer->bg[2], y);
695 break;
696 }
697 }
698 if (TEST_LAYER_ENABLED(3)) {
699 switch (GBARegisterDISPCNTGetMode(renderer->dispcnt)) {
700 case 0:
701 _drawBackgroundMode0(renderer, &renderer->bg[3], y);
702 break;
703 case 2:
704 _drawBackgroundMode2(renderer, &renderer->bg[3], y);
705 break;
706 }
707 }
708 }
709 }
710 renderer->bg[2].sx += renderer->bg[2].dmx;
711 renderer->bg[2].sy += renderer->bg[2].dmy;
712 renderer->bg[3].sx += renderer->bg[3].dmx;
713 renderer->bg[3].sy += renderer->bg[3].dmy;
714}
715
716// We stash the priority on the top bits so we can do a one-operator comparison
717// The lower the number, the higher the priority, and sprites take precendence over backgrounds
718// We want to do special processing if the color pixel is target 1, however
719
720static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
721 if (color >= current) {
722 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
723 color = _mix(renderer->blda, current, renderer->bldb, color);
724 } else {
725 color = current & 0x00FFFFFF;
726 }
727 } else {
728 color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
729 }
730 *pixel = color;
731}
732
733static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
734 // We stash the priority on the top bits so we can do a one-operator comparison
735 // The lower the number, the higher the priority, and sprites take precendence over backgrounds
736 // We want to do special processing if the color pixel is target 1, however
737 if (color >= current) {
738 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
739 color = _mix(renderer->blda, current, renderer->bldb, color);
740 } else {
741 color = current & 0x00FFFFFF;
742 }
743 } else {
744 color = color & ~FLAG_TARGET_2;
745 }
746 *pixel = color;
747}
748
749static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
750 UNUSED(renderer);
751 if (color < current) {
752 *pixel = color | (current & FLAG_OBJWIN);
753 }
754}
755
756static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, uint32_t* pixel, uint32_t color, uint32_t current) {
757 UNUSED(renderer);
758 if (color < current) {
759 *pixel = color;
760 }
761}
762
763#define COMPOSITE_16_OBJWIN(BLEND) \
764 if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
765 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
766 _composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
767 }
768
769#define COMPOSITE_16_NO_OBJWIN(BLEND) \
770 _composite ## BLEND ## NoObjwin(renderer, pixel, palette[pixelData] | flags, current);
771
772#define COMPOSITE_256_OBJWIN(BLEND) \
773 if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) { \
774 unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
775 _composite ## BLEND ## Objwin(renderer, pixel, color | flags, current); \
776 }
777
778#define COMPOSITE_256_NO_OBJWIN(BLEND) \
779 COMPOSITE_16_NO_OBJWIN(BLEND)
780
781#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN) \
782 pixelData = tileData & 0xF; \
783 current = *pixel; \
784 if (pixelData && IS_WRITABLE(current)) { \
785 COMPOSITE_16_ ## OBJWIN (BLEND); \
786 } \
787 tileData >>= 4;
788
789#define BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN) \
790 pixelData = tileData & 0xFF; \
791 current = *pixel; \
792 if (pixelData && IS_WRITABLE(current)) { \
793 COMPOSITE_256_ ## OBJWIN (BLEND); \
794 } \
795 tileData >>= 8;
796
797#define BACKGROUND_TEXT_SELECT_CHARACTER \
798 localX = tileX * 8 + inX; \
799 xBase = localX & 0xF8; \
800 if (background->size & 1) { \
801 xBase += (localX & 0x100) << 5; \
802 } \
803 screenBase = yBase + (xBase >> 3); \
804 LOAD_16(mapData, screenBase << 1, vram); \
805 localY = inY & 0x7; \
806 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
807 localY = 7 - localY; \
808 }
809
810#define PREPARE_OBJWIN \
811 int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt); \
812 int objwinOnly = 0; \
813 int objwinForceEnable = 0; \
814 color_t* objwinPalette; \
815 if (objwinSlowPath) { \
816 if (background->target1 && GBAWindowControlIsBlendEnable(renderer->objwin.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN)) { \
817 objwinPalette = renderer->variantPalette; \
818 } else { \
819 objwinPalette = renderer->normalPalette; \
820 } \
821 switch (background->index) { \
822 case 0: \
823 objwinForceEnable = GBAWindowControlIsBg0Enable(renderer->objwin.packed) && GBAWindowControlIsBg0Enable(renderer->currentWindow.packed); \
824 objwinOnly = !GBAWindowControlIsBg0Enable(renderer->objwin.packed); \
825 break; \
826 case 1: \
827 objwinForceEnable = GBAWindowControlIsBg1Enable(renderer->objwin.packed) && GBAWindowControlIsBg1Enable(renderer->currentWindow.packed); \
828 objwinOnly = !GBAWindowControlIsBg1Enable(renderer->objwin.packed); \
829 break; \
830 case 2: \
831 objwinForceEnable = GBAWindowControlIsBg2Enable(renderer->objwin.packed) && GBAWindowControlIsBg2Enable(renderer->currentWindow.packed); \
832 objwinOnly = !GBAWindowControlIsBg2Enable(renderer->objwin.packed); \
833 break; \
834 case 3: \
835 objwinForceEnable = GBAWindowControlIsBg3Enable(renderer->objwin.packed) && GBAWindowControlIsBg3Enable(renderer->currentWindow.packed); \
836 objwinOnly = !GBAWindowControlIsBg3Enable(renderer->objwin.packed); \
837 break; \
838 } \
839 }
840
841#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
842 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
843 palette = &mainPalette[paletteData]; \
844 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
845 LOAD_32(tileData, charBase, vram); \
846 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
847 tileData >>= 4 * mod8; \
848 for (; outX < end; ++outX) { \
849 uint32_t* pixel = &renderer->row[outX]; \
850 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
851 } \
852 } else { \
853 for (outX = end - 1; outX >= renderer->start; --outX) { \
854 uint32_t* pixel = &renderer->row[outX]; \
855 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
856 } \
857 }
858
859#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
860 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
861 LOAD_32(tileData, charBase, vram); \
862 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
863 palette = &mainPalette[paletteData]; \
864 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
865 outX = renderer->end - mod8; \
866 if (outX < renderer->start) { \
867 tileData >>= 4 * (renderer->start - outX); \
868 outX = renderer->start; \
869 } \
870 for (; outX < renderer->end; ++outX) { \
871 uint32_t* pixel = &renderer->row[outX]; \
872 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
873 } \
874 } else { \
875 tileData >>= 4 * (0x8 - mod8); \
876 int end2 = renderer->end - 8; \
877 if (end2 < -1) { \
878 end2 = -1; \
879 } \
880 for (outX = renderer->end - 1; outX > end2; --outX) { \
881 uint32_t* pixel = &renderer->row[outX]; \
882 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
883 } \
884 }
885
886#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
887 for (; tileX < tileEnd; ++tileX) { \
888 BACKGROUND_TEXT_SELECT_CHARACTER; \
889 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
890 tileData = carryData; \
891 for (x = 0; x < 8; ++x) { \
892 if (!mosaicWait) { \
893 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
894 palette = &mainPalette[paletteData]; \
895 LOAD_32(tileData, charBase, vram); \
896 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
897 tileData >>= x * 4; \
898 } else { \
899 tileData >>= (7 - x) * 4; \
900 } \
901 tileData &= 0xF; \
902 tileData |= tileData << 4; \
903 tileData |= tileData << 8; \
904 tileData |= tileData << 12; \
905 tileData |= tileData << 16; \
906 tileData |= tileData << 20; \
907 tileData |= tileData << 24; \
908 tileData |= tileData << 28; \
909 carryData = tileData; \
910 mosaicWait = mosaicH; \
911 } \
912 --mosaicWait; \
913 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
914 ++pixel; \
915 } \
916 }
917
918#define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \
919 for (; tileX < tileEnd; ++tileX) { \
920 BACKGROUND_TEXT_SELECT_CHARACTER; \
921 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
922 palette = &mainPalette[paletteData]; \
923 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
924 LOAD_32(tileData, charBase, vram); \
925 if (tileData) { \
926 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
927 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
928 ++pixel; \
929 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
930 ++pixel; \
931 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
932 ++pixel; \
933 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
934 ++pixel; \
935 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
936 ++pixel; \
937 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
938 ++pixel; \
939 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
940 ++pixel; \
941 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
942 ++pixel; \
943 } else { \
944 pixel += 7; \
945 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
946 --pixel; \
947 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
948 --pixel; \
949 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
950 --pixel; \
951 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
952 --pixel; \
953 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
954 --pixel; \
955 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
956 --pixel; \
957 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
958 --pixel; \
959 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
960 pixel += 8; \
961 } \
962 } else { \
963 pixel += 8; \
964 } \
965 }
966
967#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
968 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
969 int end2 = end - 4; \
970 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
971 int shift = inX & 0x3; \
972 if (end2 > outX) { \
973 LOAD_32(tileData, charBase, vram); \
974 tileData >>= 8 * shift; \
975 shift = 0; \
976 for (; outX < end2; ++outX) { \
977 pixel = &renderer->row[outX]; \
978 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
979 } \
980 } \
981 \
982 LOAD_32(tileData, charBase + 4, vram); \
983 tileData >>= 8 * shift; \
984 for (; outX < end; ++outX) { \
985 pixel = &renderer->row[outX]; \
986 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
987 } \
988 } else { \
989 int start = outX; \
990 outX = end - 1; \
991 if (end2 > start) { \
992 LOAD_32(tileData, charBase, vram); \
993 for (; outX >= end2; --outX) { \
994 pixel = &renderer->row[outX]; \
995 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
996 } \
997 charBase += 4; \
998 } \
999 \
1000 LOAD_32(tileData, charBase, vram); \
1001 for (; outX >= renderer->start; --outX) { \
1002 pixel = &renderer->row[outX]; \
1003 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1004 } \
1005 outX = end; \
1006 }
1007
1008#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
1009 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1010 outX = renderer->end - 8 + end; \
1011 int end2 = 4 - end; \
1012 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1013 if (end2 > 0) { \
1014 LOAD_32(tileData, charBase, vram); \
1015 for (; outX < renderer->end - end2; ++outX) { \
1016 pixel = &renderer->row[outX]; \
1017 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1018 } \
1019 charBase += 4; \
1020 } \
1021 \
1022 LOAD_32(tileData, charBase, vram); \
1023 for (; outX < renderer->end; ++outX) { \
1024 pixel = &renderer->row[outX]; \
1025 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1026 } \
1027 } else { \
1028 int shift = end & 0x3; \
1029 int start = outX; \
1030 outX = renderer->end - 1; \
1031 if (end2 > 0) { \
1032 LOAD_32(tileData, charBase, vram); \
1033 tileData >>= 8 * shift; \
1034 for (; outX >= start + 4; --outX) { \
1035 pixel = &renderer->row[outX]; \
1036 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1037 } \
1038 shift = 0; \
1039 } \
1040 \
1041 LOAD_32(tileData, charBase + 4, vram); \
1042 tileData >>= 8 * shift; \
1043 for (; outX >= start; --outX) { \
1044 pixel = &renderer->row[outX]; \
1045 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1046 } \
1047 }
1048
1049#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
1050 for (; tileX < tileEnd; ++tileX) { \
1051 BACKGROUND_TEXT_SELECT_CHARACTER; \
1052 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1053 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1054 LOAD_32(tileData, charBase, vram); \
1055 if (tileData) { \
1056 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1057 ++pixel; \
1058 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1059 ++pixel; \
1060 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1061 ++pixel; \
1062 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1063 ++pixel; \
1064 } else { \
1065 pixel += 4; \
1066 } \
1067 LOAD_32(tileData, charBase + 4, vram); \
1068 if (tileData) { \
1069 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1070 ++pixel; \
1071 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1072 ++pixel; \
1073 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1074 ++pixel; \
1075 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1076 ++pixel; \
1077 } else { \
1078 pixel += 4; \
1079 } \
1080 } else { \
1081 LOAD_32(tileData, charBase + 4, vram); \
1082 if (tileData) { \
1083 pixel += 3; \
1084 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1085 --pixel; \
1086 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1087 --pixel; \
1088 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1089 --pixel; \
1090 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1091 } \
1092 pixel += 4; \
1093 LOAD_32(tileData, charBase, vram); \
1094 if (tileData) { \
1095 pixel += 3; \
1096 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1097 --pixel; \
1098 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1099 --pixel; \
1100 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1101 --pixel; \
1102 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1103 } \
1104 pixel += 4; \
1105 } \
1106 }
1107
1108#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
1109 for (; tileX < tileEnd; ++tileX) { \
1110 BACKGROUND_TEXT_SELECT_CHARACTER; \
1111 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
1112 tileData = carryData; \
1113 for (x = 0; x < 8; ++x) { \
1114 if (!mosaicWait) { \
1115 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
1116 if (x >= 4) { \
1117 LOAD_32(tileData, charBase + 4, vram); \
1118 tileData >>= (x - 4) * 8; \
1119 } else { \
1120 LOAD_32(tileData, charBase, vram); \
1121 tileData >>= x * 8; \
1122 } \
1123 } else { \
1124 if (x >= 4) { \
1125 LOAD_32(tileData, charBase, vram); \
1126 tileData >>= (7 - x) * 8; \
1127 } else { \
1128 LOAD_32(tileData, charBase + 4, vram); \
1129 tileData >>= (3 - x) * 8; \
1130 } \
1131 } \
1132 tileData &= 0xFF; \
1133 carryData = tileData; \
1134 mosaicWait = mosaicH; \
1135 } \
1136 tileData |= tileData << 8; \
1137 --mosaicWait; \
1138 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
1139 ++pixel; \
1140 } \
1141 }
1142
1143#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
1144 uint32_t* pixel = &renderer->row[outX]; \
1145 if (background->mosaic && renderer->mosaic.bgH) { \
1146 int mosaicH = renderer->mosaic.bgH + 1; \
1147 int x; \
1148 int mosaicWait = outX % mosaicH; \
1149 int carryData = 0; \
1150 paletteData = 0; /* Quiets compiler warning */ \
1151 DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
1152 return; \
1153 } \
1154 \
1155 if (inX & 0x7) { \
1156 int mod8 = inX & 0x7; \
1157 BACKGROUND_TEXT_SELECT_CHARACTER; \
1158 \
1159 int end = outX + 0x8 - mod8; \
1160 if (end > renderer->end) { \
1161 /* TODO: ensure tiles are properly aligned from this*/ \
1162 end = renderer->end; \
1163 } \
1164 if (end == outX) { \
1165 return; \
1166 } \
1167 DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
1168 } \
1169 if (inX & 0x7 || (renderer->end - renderer->start) & 0x7) { \
1170 tileX = tileEnd; \
1171 int pixelData; \
1172 int mod8 = (inX + renderer->end - renderer->start) & 0x7; \
1173 BACKGROUND_TEXT_SELECT_CHARACTER; \
1174 \
1175 int end = 0x8 - mod8; \
1176 UNUSED(end); \
1177 DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
1178 \
1179 tileX = (inX & 0x7) != 0; \
1180 outX = renderer->start + tileX * 8 - (inX & 0x7); \
1181 } \
1182 \
1183 pixel = &renderer->row[outX]; \
1184 DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN)
1185
1186static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
1187 int inX = renderer->start + background->x;
1188 if (background->mosaic) {
1189 int mosaicV = renderer->mosaic.bgV + 1;
1190 y -= y % mosaicV;
1191 }
1192 int inY = y + background->y;
1193 uint16_t mapData;
1194
1195 unsigned yBase = inY & 0xF8;
1196 if (background->size == 2) {
1197 yBase += inY & 0x100;
1198 } else if (background->size == 3) {
1199 yBase += (inY & 0x100) << 1;
1200 }
1201 yBase = (background->screenBase >> 1) + (yBase << 2);
1202
1203 int localX;
1204 int localY;
1205
1206 unsigned xBase;
1207
1208 int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
1209 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
1210 flags |= FLAG_TARGET_2 * background->target2;
1211
1212 uint32_t screenBase;
1213 uint32_t charBase;
1214 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1215 color_t* mainPalette = renderer->normalPalette;
1216 if (variant) {
1217 mainPalette = renderer->variantPalette;
1218 }
1219 color_t* palette = mainPalette;
1220 PREPARE_OBJWIN;
1221
1222 int outX = renderer->start;
1223
1224 uint32_t tileData;
1225 uint32_t current;
1226 int pixelData;
1227 int paletteData;
1228 int tileX = 0;
1229 int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3;
1230 uint16_t* vram = renderer->d.vram;
1231
1232 if (!objwinSlowPath) {
1233 if (!(flags & FLAG_TARGET_2)) {
1234 if (!background->multipalette) {
1235 DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
1236 } else {
1237 DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
1238 }
1239 } else {
1240 if (!background->multipalette) {
1241 DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
1242 } else {
1243 DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
1244 }
1245 }
1246 } else {
1247 if (!(flags & FLAG_TARGET_2)) {
1248 if (!background->multipalette) {
1249 DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
1250 } else {
1251 DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
1252 }
1253 } else {
1254 if (!background->multipalette) {
1255 DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
1256 } else {
1257 DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
1258 }
1259 }
1260 }
1261}
1262
1263#define BACKGROUND_BITMAP_INIT \
1264 UNUSED(unused); \
1265 int32_t x = background->sx + (renderer->start - 1) * background->dx; \
1266 int32_t y = background->sy + (renderer->start - 1) * background->dy; \
1267 int32_t localX; \
1268 int32_t localY; \
1269 \
1270 int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \
1271 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
1272 flags |= FLAG_TARGET_2 * background->target2; \
1273 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \
1274 color_t* palette = renderer->normalPalette; \
1275 if (variant) { \
1276 palette = renderer->variantPalette; \
1277 } \
1278 PREPARE_OBJWIN;
1279
1280#define BACKGROUND_BITMAP_ITERATE(W, H) \
1281 x += background->dx; \
1282 y += background->dy; \
1283 \
1284 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1285 continue; \
1286 } else { \
1287 localX = x; \
1288 localY = y; \
1289 }
1290
1291static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1292 int sizeAdjusted = 0x8000 << background->size;
1293
1294 BACKGROUND_BITMAP_INIT;
1295
1296 uint32_t screenBase = background->screenBase;
1297 uint32_t charBase = background->charBase;
1298 uint8_t mapData;
1299 uint8_t tileData;
1300
1301 int outX;
1302 uint32_t* pixel;
1303 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1304 x += background->dx;
1305 y += background->dy;
1306
1307 if (background->overflow) {
1308 localX = x & (sizeAdjusted - 1);
1309 localY = y & (sizeAdjusted - 1);
1310 } else if ((x | y) & ~(sizeAdjusted - 1)) {
1311 continue;
1312 } else {
1313 localX = x;
1314 localY = y;
1315 }
1316 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1317 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1318
1319 uint32_t current = *pixel;
1320 if (tileData && IS_WRITABLE(current)) {
1321 if (!objwinSlowPath) {
1322 _compositeBlendNoObjwin(renderer, pixel, palette[tileData] | flags, current);
1323 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1324 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1325 _compositeBlendObjwin(renderer, pixel, currentPalette[tileData] | flags, current);
1326 }
1327 }
1328 }
1329}
1330
1331static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1332 BACKGROUND_BITMAP_INIT;
1333
1334 uint32_t color;
1335
1336 int outX;
1337 uint32_t* pixel;
1338 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1339 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1340
1341 LOAD_16(color, ((localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS) << 1, renderer->d.vram);
1342#ifndef COLOR_16_BIT
1343 unsigned color32;
1344 color32 = 0;
1345 color32 |= (color << 3) & 0xF8;
1346 color32 |= (color << 6) & 0xF800;
1347 color32 |= (color << 9) & 0xF80000;
1348 color = color32;
1349#endif
1350
1351 uint32_t current = *pixel;
1352 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1353 if (!variant) {
1354 _compositeBlendObjwin(renderer, pixel, color | flags, current);
1355 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1356 _compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1357 } else if (renderer->blendEffect == BLEND_DARKEN) {
1358 _compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1359 }
1360 }
1361 }
1362}
1363
1364static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1365 BACKGROUND_BITMAP_INIT;
1366
1367 uint16_t color;
1368 uint32_t offset = 0;
1369 if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
1370 offset = 0xA000;
1371 }
1372
1373 int outX;
1374 uint32_t* pixel;
1375 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1376 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1377
1378 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1379
1380 uint32_t current = *pixel;
1381 if (color && IS_WRITABLE(current)) {
1382 if (!objwinSlowPath) {
1383 _compositeBlendNoObjwin(renderer, pixel, palette[color] | flags, current);
1384 } else if (objwinForceEnable || !(current & FLAG_OBJWIN) == objwinOnly) {
1385 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
1386 _compositeBlendObjwin(renderer, pixel, currentPalette[color] | flags, current);
1387 }
1388 }
1389 }
1390}
1391
1392static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1393 BACKGROUND_BITMAP_INIT;
1394
1395 uint32_t color;
1396 uint32_t offset = 0;
1397 if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
1398 offset = 0xA000;
1399 }
1400
1401 int outX;
1402 uint32_t* pixel;
1403 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) {
1404 BACKGROUND_BITMAP_ITERATE(160, 128);
1405
1406 LOAD_16(color, (offset + (localX >> 8) + (localY >> 8) * 160) << 1, renderer->d.vram);
1407#ifndef COLOR_16_BIT
1408 unsigned color32 = 0;
1409 color32 |= (color << 9) & 0xF80000;
1410 color32 |= (color << 3) & 0xF8;
1411 color32 |= (color << 6) & 0xF800;
1412 color = color32;
1413#endif
1414
1415 uint32_t current = *pixel;
1416 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) {
1417 if (!variant) {
1418 _compositeBlendObjwin(renderer, pixel, color | flags, current);
1419 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1420 _compositeBlendObjwin(renderer, pixel, _brighten(color, renderer->bldy) | flags, current);
1421 } else if (renderer->blendEffect == BLEND_DARKEN) {
1422 _compositeBlendObjwin(renderer, pixel, _darken(color, renderer->bldy) | flags, current);
1423 }
1424 }
1425 }
1426}
1427
1428#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1429 SPRITE_YBASE_ ## DEPTH(inY); \
1430 unsigned tileData; \
1431 for (; outX < condition; ++outX, inX += xOffset) { \
1432 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1433 continue; \
1434 } \
1435 SPRITE_XBASE_ ## DEPTH(inX); \
1436 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1437 }
1438
1439#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
1440 SPRITE_YBASE_ ## DEPTH(inY); \
1441 unsigned tileData; \
1442 if (outX % mosaicH) { \
1443 inX += (mosaicH - (outX % mosaicH)) * xOffset; \
1444 outX += mosaicH - (outX % mosaicH); \
1445 } \
1446 for (; outX < condition; ++outX, inX += xOffset) { \
1447 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1448 continue; \
1449 } \
1450 int localX = inX - xOffset * (outX % mosaicH); \
1451 SPRITE_XBASE_ ## DEPTH(localX); \
1452 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1453 }
1454
1455#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1456 unsigned tileData; \
1457 for (; outX < x + totalWidth && outX < end; ++outX, ++inX) { \
1458 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1459 continue; \
1460 } \
1461 xAccum += mat.a; \
1462 yAccum += mat.c; \
1463 int localX = (xAccum >> 8) + (width >> 1); \
1464 int localY = (yAccum >> 8) + (height >> 1); \
1465 \
1466 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1467 continue; \
1468 } \
1469 \
1470 SPRITE_YBASE_ ## DEPTH(localY); \
1471 SPRITE_XBASE_ ## DEPTH(localX); \
1472 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1473 }
1474
1475#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1476#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1477
1478#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1479 LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1480 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1481 if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
1482 renderer->spriteLayer[outX] = palette[tileData] | flags; \
1483 }
1484
1485#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1486 LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1487 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1488 if (tileData) { \
1489 renderer->row[outX] |= FLAG_OBJWIN; \
1490 }
1491
1492#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1493#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? width : 0x80) + (localY & 0x7) * 8;
1494
1495#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1496 LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1497 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1498 if (tileData && (renderer->spriteLayer[outX] & FLAG_ORDER_MASK) > flags) { \
1499 renderer->spriteLayer[outX] = palette[tileData] | flags; \
1500 }
1501
1502#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1503 LOAD_16(tileData, ((yBase + charBase + xBase) & 0x7FFF), vramBase); \
1504 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1505 if (tileData) { \
1506 renderer->row[outX] |= FLAG_OBJWIN; \
1507 }
1508
1509static int _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1510 int width = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2];
1511 int height = _objSizes[GBAObjAttributesAGetShape(sprite->a) * 8 + GBAObjAttributesBGetSize(sprite->b) * 2 + 1];
1512 int start = renderer->start;
1513 int end = renderer->end;
1514 uint32_t flags = GBAObjAttributesCGetPriority(sprite->c) << OFFSET_PRIORITY;
1515 flags |= FLAG_TARGET_1 * ((GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT);
1516 flags |= FLAG_OBJWIN * (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN);
1517 int32_t x = GBAObjAttributesBGetX(sprite->b) << 23;
1518 x >>= 23;
1519 uint16_t* vramBase = &renderer->d.vram[BASE_TILE >> 1];
1520 unsigned charBase = GBAObjAttributesCGetTile(sprite->c) * 0x20;
1521 int variant = renderer->target1Obj && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1522 if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT && renderer->target2Bd) {
1523 // Hack: if a sprite is blended, then the variant palette is not used, but we don't know if it's blended in advance
1524 variant = 0;
1525 }
1526 color_t* palette = &renderer->normalPalette[0x100];
1527 if (variant) {
1528 palette = &renderer->variantPalette[0x100];
1529 }
1530
1531 int inY = y - (int) GBAObjAttributesAGetY(sprite->a);
1532
1533 if (GBAObjAttributesAIsTransformed(sprite->a)) {
1534 int totalWidth = width << GBAObjAttributesAGetDoubleSize(sprite->a);
1535 int totalHeight = height << GBAObjAttributesAGetDoubleSize(sprite->a);
1536 struct GBAOAMMatrix mat;
1537 LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);
1538 LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b);
1539 LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c);
1540 LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
1541
1542 if (inY < 0) {
1543 inY += 256;
1544 }
1545 int outX = x >= start ? x : start;
1546 int inX = outX - x;
1547 int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1));
1548 int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1));
1549
1550 if (!GBAObjAttributesAIs256Color(sprite->a)) {
1551 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
1552 if (flags & FLAG_OBJWIN) {
1553 SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1554 } else {
1555 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1556 }
1557 } else {
1558 if (flags & FLAG_OBJWIN) {
1559 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1560 } else {
1561 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1562 }
1563 }
1564 } else {
1565 int outX = x >= start ? x : start;
1566 int condition = x + width;
1567 int mosaicH = 1;
1568 if (GBAObjAttributesAIsMosaic(sprite->a)) {
1569 mosaicH = renderer->mosaic.objH + 1;
1570 if (condition % mosaicH) {
1571 condition += mosaicH - (condition % mosaicH);
1572 }
1573 }
1574 if ((int) GBAObjAttributesAGetY(sprite->a) + height - 256 >= 0) {
1575 inY += 256;
1576 }
1577 if (GBAObjAttributesBIsVFlip(sprite->b)) {
1578 inY = height - inY - 1;
1579 }
1580 if (end < condition) {
1581 condition = end;
1582 }
1583 int inX = outX - x;
1584 int xOffset = 1;
1585 if (GBAObjAttributesBIsHFlip(sprite->b)) {
1586 inX = width - inX - 1;
1587 xOffset = -1;
1588 }
1589 if (!GBAObjAttributesAIs256Color(sprite->a)) {
1590 palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
1591 if (flags & FLAG_OBJWIN) {
1592 SPRITE_NORMAL_LOOP(16, OBJWIN);
1593 } else if (GBAObjAttributesAIsMosaic(sprite->a)) {
1594 SPRITE_MOSAIC_LOOP(16, NORMAL);
1595 } else {
1596 SPRITE_NORMAL_LOOP(16, NORMAL);
1597 }
1598 } else {
1599 if (flags & FLAG_OBJWIN) {
1600 SPRITE_NORMAL_LOOP(256, OBJWIN);
1601 } else if (GBAObjAttributesAIsMosaic(sprite->a)) {
1602 SPRITE_MOSAIC_LOOP(256, NORMAL);
1603 } else {
1604 SPRITE_NORMAL_LOOP(256, NORMAL);
1605 }
1606 }
1607 }
1608 return 1;
1609}
1610
1611static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1612 int x;
1613 uint32_t* pixel = renderer->row;
1614 uint32_t flags = FLAG_TARGET_2 * renderer->target2Obj;
1615
1616 int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt);
1617 int objwinDisable = 0;
1618 if (objwinSlowPath) {
1619 objwinDisable = !GBAWindowControlIsObjEnable(renderer->objwin.packed);
1620 }
1621 if (objwinSlowPath && objwinDisable) {
1622 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1623 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1624 uint32_t current = *pixel;
1625 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && !(current & FLAG_OBJWIN) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1626 _compositeBlendObjwin(renderer, pixel, color | flags, current);
1627 }
1628 }
1629 } else {
1630 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x, ++pixel) {
1631 uint32_t color = renderer->spriteLayer[x] & ~FLAG_OBJWIN;
1632 uint32_t current = *pixel;
1633 if ((color & FLAG_UNWRITTEN) != FLAG_UNWRITTEN && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority) {
1634 _compositeBlendNoObjwin(renderer, pixel, color | flags, current);
1635 }
1636 }
1637 }
1638}
1639
1640static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1641 int i;
1642 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1643 for (i = 0; i < 512; ++i) {
1644 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1645 }
1646 } else if (renderer->blendEffect == BLEND_DARKEN) {
1647 for (i = 0; i < 512; ++i) {
1648 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1649 }
1650 } else {
1651 for (i = 0; i < 512; ++i) {
1652 renderer->variantPalette[i] = renderer->normalPalette[i];
1653 }
1654 }
1655}
1656
1657static inline unsigned _brighten(unsigned color, int y) {
1658 unsigned c = 0;
1659 unsigned a;
1660#ifdef COLOR_16_BIT
1661 a = color & 0x1F;
1662 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1663
1664#ifdef COLOR_5_6_5
1665 a = color & 0x7C0;
1666 c |= (a + ((0x7C0 - a) * y) / 16) & 0x7C0;
1667
1668 a = color & 0xF800;
1669 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1670#else
1671 a = color & 0x3E0;
1672 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1673
1674 a = color & 0x7C00;
1675 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1676#endif
1677#else
1678 a = color & 0xF8;
1679 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1680
1681 a = color & 0xF800;
1682 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1683
1684 a = color & 0xF80000;
1685 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1686#endif
1687 return c;
1688}
1689
1690static inline unsigned _darken(unsigned color, int y) {
1691 unsigned c = 0;
1692 unsigned a;
1693#ifdef COLOR_16_BIT
1694 a = color & 0x1F;
1695 c |= (a - (a * y) / 16) & 0x1F;
1696
1697#ifdef COLOR_5_6_5
1698 a = color & 0x7C0;
1699 c |= (a - (a * y) / 16) & 0x7C0;
1700
1701 a = color & 0xF800;
1702 c |= (a - (a * y) / 16) & 0xF800;
1703#else
1704 a = color & 0x3E0;
1705 c |= (a - (a * y) / 16) & 0x3E0;
1706
1707 a = color & 0x7C00;
1708 c |= (a - (a * y) / 16) & 0x7C00;
1709#endif
1710#else
1711 a = color & 0xF8;
1712 c |= (a - (a * y) / 16) & 0xF8;
1713
1714 a = color & 0xF800;
1715 c |= (a - (a * y) / 16) & 0xF800;
1716
1717 a = color & 0xF80000;
1718 c |= (a - (a * y) / 16) & 0xF80000;
1719#endif
1720 return c;
1721}
1722
1723static unsigned _mix(int weightA, unsigned colorA, int weightB, unsigned colorB) {
1724 unsigned c = 0;
1725 unsigned a, b;
1726#ifdef COLOR_16_BIT
1727#ifdef COLOR_5_6_5
1728 a = colorA & 0xF81F;
1729 b = colorB & 0xF81F;
1730 a |= (colorA & 0x7C0) << 16;
1731 b |= (colorB & 0x7C0) << 16;
1732 c = ((a * weightA + b * weightB) / 16);
1733 if (c & 0x08000000) {
1734 c = (c & ~0x0FC00000) | 0x07C00000;
1735 }
1736 if (c & 0x0020) {
1737 c = (c & ~0x003F) | 0x001F;
1738 }
1739 if (c & 0x10000) {
1740 c = (c & ~0x1F800) | 0xF800;
1741 }
1742 c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
1743#else
1744 a = colorA & 0x7C1F;
1745 b = colorB & 0x7C1F;
1746 a |= (colorA & 0x3E0) << 16;
1747 b |= (colorB & 0x3E0) << 16;
1748 c = ((a * weightA + b * weightB) / 16);
1749 if (c & 0x04000000) {
1750 c = (c & ~0x07E00000) | 0x03E00000;
1751 }
1752 if (c & 0x0020) {
1753 c = (c & ~0x003F) | 0x001F;
1754 }
1755 if (c & 0x10000) {
1756 c = (c & ~0x1F800) | 0xF800;
1757 }
1758 c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
1759#endif
1760#else
1761 a = colorA & 0xF8;
1762 b = colorB & 0xF8;
1763 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1764 if (c & 0x00000100) {
1765 c = 0x000000F8;
1766 }
1767
1768 a = colorA & 0xF800;
1769 b = colorB & 0xF800;
1770 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1771 if (c & 0x00010000) {
1772 c = (c & 0x000000F8) | 0x0000F800;
1773 }
1774
1775 a = colorA & 0xF80000;
1776 b = colorB & 0xF80000;
1777 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1778 if (c & 0x01000000) {
1779 c = (c & 0x0000F8F8) | 0x00F80000;
1780 }
1781#endif
1782 return c;
1783}