all repos — mgba @ 13a46429e200afbc4a6138fcb335bd1e269deaf4

mGBA Game Boy Advance Emulator

src/gba/renderers/video-software.c (view raw)

  1#include "video-software.h"
  2
  3#include "gba.h"
  4#include "gba-io.h"
  5
  6#include <string.h>
  7
  8static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
  9static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
 10static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 11static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 12static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
 13static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
 14
 15static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
 16static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
 17static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
 18
 19static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
 20static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
 21static void _drawTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
 22static void _drawSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
 23
 24static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
 25static inline uint32_t _brighten(uint32_t color, int y);
 26static inline uint32_t _darken(uint32_t color, int y);
 27static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB);
 28
 29void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
 30	renderer->d.init = GBAVideoSoftwareRendererInit;
 31	renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
 32	renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
 33	renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
 34	renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
 35	renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
 36
 37	renderer->d.turbo = 0;
 38	renderer->d.framesPending = 0;
 39
 40	{
 41		pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 42		renderer->mutex = mutex;
 43		pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
 44		renderer->upCond = cond;
 45		renderer->downCond = cond;
 46	}
 47}
 48
 49static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
 50	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 51	int i;
 52
 53	softwareRenderer->dispcnt.packed = 0x0080;
 54
 55	softwareRenderer->target1Obj = 0;
 56	softwareRenderer->target1Bd = 0;
 57	softwareRenderer->target2Obj = 0;
 58	softwareRenderer->target2Bd = 0;
 59	softwareRenderer->blendEffect = BLEND_NONE;
 60	memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
 61	memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
 62
 63	softwareRenderer->blda = 0;
 64	softwareRenderer->bldb = 0;
 65	softwareRenderer->bldy = 0;
 66
 67	for (i = 0; i < 4; ++i) {
 68		struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
 69		bg->index = i;
 70		bg->enabled = 0;
 71		bg->priority = 0;
 72		bg->charBase = 0;
 73		bg->mosaic = 0;
 74		bg->multipalette = 0;
 75		bg->screenBase = 0;
 76		bg->overflow = 0;
 77		bg->size = 0;
 78		bg->target1 = 0;
 79		bg->target2 = 0;
 80		bg->x = 0;
 81		bg->y = 0;
 82		bg->refx = 0;
 83		bg->refy = 0;
 84		bg->dx = 256;
 85		bg->dmx = 0;
 86		bg->dy = 0;
 87		bg->dmy = 256;
 88		bg->sx = 0;
 89		bg->sy = 0;
 90	}
 91
 92	pthread_mutex_init(&softwareRenderer->mutex, 0);
 93	pthread_cond_init(&softwareRenderer->upCond, 0);
 94	pthread_cond_init(&softwareRenderer->downCond, 0);
 95}
 96
 97static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
 98	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
 99
100	pthread_mutex_destroy(&softwareRenderer->mutex);
101	pthread_cond_destroy(&softwareRenderer->upCond);
102	pthread_cond_destroy(&softwareRenderer->downCond);
103}
104
105static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
106	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
107	switch (address) {
108	case REG_DISPCNT:
109		value &= 0xFFFB;
110		softwareRenderer->dispcnt.packed = value;
111		GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
112		break;
113	case REG_BG0CNT:
114		value &= 0xFFCF;
115		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
116		break;
117	case REG_BG1CNT:
118		value &= 0xFFCF;
119		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
120		break;
121	case REG_BG2CNT:
122		value &= 0xFFCF;
123		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
124		break;
125	case REG_BG3CNT:
126		value &= 0xFFCF;
127		GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
128		break;
129	case REG_BG0HOFS:
130		value &= 0x01FF;
131		softwareRenderer->bg[0].x = value;
132		break;
133	case REG_BG0VOFS:
134		value &= 0x01FF;
135		softwareRenderer->bg[0].y = value;
136		break;
137	case REG_BG1HOFS:
138		value &= 0x01FF;
139		softwareRenderer->bg[1].x = value;
140		break;
141	case REG_BG1VOFS:
142		value &= 0x01FF;
143		softwareRenderer->bg[1].y = value;
144		break;
145	case REG_BG2HOFS:
146		value &= 0x01FF;
147		softwareRenderer->bg[2].x = value;
148		break;
149	case REG_BG2VOFS:
150		value &= 0x01FF;
151		softwareRenderer->bg[2].y = value;
152		break;
153	case REG_BG3HOFS:
154		value &= 0x01FF;
155		softwareRenderer->bg[3].x = value;
156		break;
157	case REG_BG3VOFS:
158		value &= 0x01FF;
159		softwareRenderer->bg[3].y = value;
160		break;
161	case REG_BLDCNT:
162		GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
163		break;
164	case REG_BLDALPHA:
165		softwareRenderer->blda = value & 0x1F;
166		if (softwareRenderer->blda > 0x10) {
167			softwareRenderer->blda = 0x10;
168		}
169		softwareRenderer->bldb = (value >> 8) & 0x1F;
170		if (softwareRenderer->bldb > 0x10) {
171			softwareRenderer->bldb = 0x10;
172		}
173		break;
174	case REG_BLDY:
175		softwareRenderer->bldy = value & 0x1F;
176		if (softwareRenderer->bldy > 0x10) {
177			softwareRenderer->bldy = 0x10;
178		}
179		_updatePalettes(softwareRenderer);
180		break;
181	default:
182		GBALog(GBA_LOG_STUB, "Stub video register write: %03x", address);
183	}
184	return value;
185}
186
187static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
188	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
189	uint32_t color32 = 0;
190	if (address & 0x1F) {
191		color32 = 0xFF000000;
192	}
193	color32 |= (value << 3) & 0xF8;
194	color32 |= (value << 6) & 0xF800;
195	color32 |= (value << 9) & 0xF80000;
196	softwareRenderer->normalPalette[address >> 1] = color32;
197	if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
198		softwareRenderer->variantPalette[address >> 1] = _brighten(color32, softwareRenderer->bldy);
199	} else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
200		softwareRenderer->variantPalette[address >> 1] = _darken(color32, softwareRenderer->bldy);
201	}
202}
203
204static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
205	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
206	uint32_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
207	if (softwareRenderer->dispcnt.forcedBlank) {
208		for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
209			row[x] = GBA_COLOR_WHITE;
210		}
211		return;
212	} else {
213		uint32_t backdrop;
214		if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA) {
215			backdrop = softwareRenderer->normalPalette[0];
216		} else {
217			backdrop = softwareRenderer->variantPalette[0];
218		}
219		for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
220			row[x] = backdrop;
221		}
222	}
223
224	memset(softwareRenderer->flags, 0, sizeof(softwareRenderer->flags));
225	memset(softwareRenderer->spriteLayer, 0, sizeof(softwareRenderer->spriteLayer));
226	softwareRenderer->row = row;
227
228	softwareRenderer->start = 0;
229	softwareRenderer->end = VIDEO_HORIZONTAL_PIXELS;
230	_drawScanline(softwareRenderer, y);
231}
232
233static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
234	struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
235
236	pthread_mutex_lock(&softwareRenderer->mutex);
237	renderer->framesPending++;
238	pthread_cond_broadcast(&softwareRenderer->upCond);
239	if (!renderer->turbo) {
240		pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
241	}
242	pthread_mutex_unlock(&softwareRenderer->mutex);
243}
244
245static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
246	renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
247	renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
248	renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
249	renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
250}
251
252static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
253	(void)(renderer);
254	union GBARegisterBGCNT reg = { .packed = value };
255	bg->priority = reg.priority;
256	bg->charBase = reg.charBase << 14;
257	bg->mosaic = reg.mosaic;
258	bg->multipalette = reg.multipalette;
259	bg->screenBase = reg.screenBase << 11;
260	bg->overflow = reg.overflow;
261	bg->size = reg.size;
262}
263
264static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
265	union {
266		struct {
267			unsigned target1Bg0 : 1;
268			unsigned target1Bg1 : 1;
269			unsigned target1Bg2 : 1;
270			unsigned target1Bg3 : 1;
271			unsigned target1Obj : 1;
272			unsigned target1Bd : 1;
273			enum BlendEffect effect : 2;
274			unsigned target2Bg0 : 1;
275			unsigned target2Bg1 : 1;
276			unsigned target2Bg2 : 1;
277			unsigned target2Bg3 : 1;
278			unsigned target2Obj : 1;
279			unsigned target2Bd : 1;
280		};
281		uint16_t packed;
282	} bldcnt = { .packed = value };
283
284	enum BlendEffect oldEffect = renderer->blendEffect;
285
286	renderer->bg[0].target1 = bldcnt.target1Bg0;
287	renderer->bg[1].target1 = bldcnt.target1Bg1;
288	renderer->bg[2].target1 = bldcnt.target1Bg2;
289	renderer->bg[3].target1 = bldcnt.target1Bg3;
290	renderer->bg[0].target2 = bldcnt.target2Bg0;
291	renderer->bg[1].target2 = bldcnt.target2Bg1;
292	renderer->bg[2].target2 = bldcnt.target2Bg2;
293	renderer->bg[3].target2 = bldcnt.target2Bg3;
294
295	renderer->blendEffect = bldcnt.effect;
296	renderer->target1Obj = bldcnt.target1Obj;
297	renderer->target1Bd = bldcnt.target1Bd;
298	renderer->target2Obj = bldcnt.target2Obj;
299	renderer->target2Bd = bldcnt.target2Bd;
300
301	if (oldEffect != renderer->blendEffect) {
302		_updatePalettes(renderer);
303	}
304}
305
306static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
307	int i;
308	if (renderer->dispcnt.objEnable) {
309		for (i = 0; i < 128; ++i) {
310			struct GBAObj* sprite = &renderer->d.oam->obj[i];
311			if (sprite->transformed) {
312				_drawTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
313			} else if (!sprite->disable) {
314				_drawSprite(renderer, sprite, y);
315			}
316		}
317	}
318
319	int priority;
320	for (priority = 0; priority < 4; ++priority) {
321		for (i = 0; i < 4; ++i) {
322			if (renderer->bg[i].enabled && renderer->bg[i].priority == priority) {
323				_drawBackgroundMode0(renderer, &renderer->bg[i], y);
324			}
325		}
326	}
327}
328
329static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color, struct PixelFlags flags) {
330	struct PixelFlags currentFlags = renderer->flags[offset];
331	if (currentFlags.isSprite && flags.priority >= currentFlags.priority) {
332		if (currentFlags.target1) {
333			if (currentFlags.written && currentFlags.target2) {
334				renderer->row[offset] = _mix(renderer->blda, renderer->row[offset], renderer->bldb, renderer->spriteLayer[offset]);
335			} else if (flags.target2) {
336				renderer->row[offset] = _mix(renderer->bldb, color, renderer->blda, renderer->spriteLayer[offset]);
337			}
338		} else if (!currentFlags.written) {
339			renderer->row[offset] = renderer->spriteLayer[offset];
340		}
341		renderer->flags[offset].finalized = 1;
342		return;
343	}
344	if (renderer->blendEffect != BLEND_ALPHA) {
345		renderer->row[offset] = color;
346		renderer->flags[offset].finalized = 1;
347	} else if (renderer->blendEffect == BLEND_ALPHA) {
348		if (currentFlags.written) {
349			if (currentFlags.target1 && flags.target2) {
350				renderer->row[offset] = _mix(renderer->bldb, color, renderer->blda, renderer->row[offset]);
351			}
352			renderer->flags[offset].finalized = 1;
353		} else {
354			renderer->row[offset] = color;
355			renderer->flags[offset].target1 = flags.target1;
356		}
357	}
358	renderer->flags[offset].written = 1;
359}
360
361#define BACKGROUND_DRAW_PIXEL_16_NORMAL \
362	if (tileData & 0xF && !renderer->flags[outX].finalized) { \
363		_composite(renderer, outX, renderer->normalPalette[tileData & 0xF | (mapData.palette << 4)], flags); \
364	} \
365	tileData >>= 4;
366
367#define BACKGROUND_DRAW_PIXEL_16_VARIANT \
368	if (tileData & 0xF && !renderer->flags[outX].finalized) { \
369		_composite(renderer, outX, renderer->variantPalette[tileData & 0xF | (mapData.palette << 4)], flags); \
370	} \
371	tileData >>= 4;
372
373#define BACKGROUND_DRAW_PIXEL_256_NORMAL \
374	if (tileData & 0xFF && !renderer->flags[outX].finalized) { \
375		_composite(renderer, outX, renderer->normalPalette[tileData & 0xFF], flags); \
376	} \
377	tileData >>= 8;
378
379#define BACKGROUND_DRAW_PIXEL_256_VARIANT \
380	if (tileData & 0xFF && !renderer->flags[outX].finalized) { \
381		_composite(renderer, outX, renderer->variantPalette[tileData & 0xFF], flags); \
382	} \
383	tileData >>= 8;
384
385#define BACKGROUND_TEXT_SELECT_CHARACTER \
386	localX = tileX * 8 + inX; \
387	xBase = localX & 0xF8; \
388	if (background->size & 1) { \
389		xBase += (localX & 0x100) << 5; \
390	} \
391	screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
392	mapData.packed = renderer->d.vram[screenBase]; \
393	if (!mapData.vflip) { \
394		localY = inY & 0x7; \
395	} else { \
396		localY = 7 - (inY & 0x7); \
397	}
398
399#define BACKGROUND_MODE_0_TILE_16_LOOP(TYPE) \
400	for (; tileX < 30; ++tileX) { \
401		BACKGROUND_TEXT_SELECT_CHARACTER; \
402		charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY; \
403		uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
404		if (tileData) { \
405			if (!mapData.hflip) { \
406				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
407				++outX; \
408				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
409				++outX; \
410				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
411				++outX; \
412				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
413				++outX; \
414				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
415				++outX; \
416				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
417				++outX; \
418				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
419				++outX; \
420				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
421				++outX; \
422			} else { \
423				outX += 7; \
424				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
425				--outX; \
426				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
427				--outX; \
428				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
429				--outX; \
430				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
431				--outX; \
432				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
433				--outX; \
434				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
435				--outX; \
436				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
437				--outX; \
438				BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
439				outX += 8; \
440			} \
441		} else { \
442			outX += 8; \
443		} \
444	}
445
446#define BACKGROUND_MODE_0_TILE_256_LOOP(TYPE) \
447		for (; tileX < 30; ++tileX) { \
448			BACKGROUND_TEXT_SELECT_CHARACTER; \
449			charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1); \
450			if (!mapData.hflip) { \
451				uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
452				if (tileData) { \
453						BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
454						++outX; \
455						BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
456						++outX; \
457						BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
458						++outX; \
459						BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
460						++outX; \
461				} else { \
462					outX += 4; \
463				} \
464				tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
465				if (tileData) { \
466						BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
467						++outX; \
468						BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
469						++outX; \
470						BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
471						++outX; \
472						BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
473						++outX; \
474				} else { \
475					outX += 4; \
476				} \
477			} else { \
478				uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
479				if (tileData) { \
480					outX += 3; \
481					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
482					--outX; \
483					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
484					--outX; \
485					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
486					--outX; \
487					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
488					outX += 4; \
489				} else { \
490					outX += 4; \
491				} \
492				tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
493				if (tileData) { \
494					outX += 3; \
495					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
496					--outX; \
497					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
498					--outX; \
499					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
500					--outX; \
501					BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
502					outX += 4; \
503				} else { \
504					outX += 4; \
505				} \
506			} \
507		}
508
509static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
510	int inX = background->x;
511	int inY = y + background->y;
512	union GBATextMapData mapData;
513
514	unsigned yBase = inY & 0xF8;
515	if (background->size & 2) {
516		yBase += inY & 0x100;
517	} else if (background->size == 3) {
518		yBase += (inY & 0x100) << 1;
519	}
520
521	int localX;
522	int localY;
523
524	unsigned xBase;
525
526	struct PixelFlags flags = {
527		.target1 = background->target1 && renderer->blendEffect == BLEND_ALPHA,
528		.target2 = background->target2,
529		.priority = background->priority
530	};
531
532	uint32_t screenBase;
533	uint32_t charBase;
534	int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
535
536	int outX = 0;
537	int tileX = 0;
538	if (inX & 0x7) {
539		int end = 0x8 - (inX & 0x7);
540		uint32_t tileData;
541		BACKGROUND_TEXT_SELECT_CHARACTER;
542		charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
543		tileData = ((uint32_t*)renderer->d.vram)[charBase];
544		tileData >>= 4 * (inX & 0x7);
545		if (!variant) {
546			for (outX = 0; outX < end; ++outX) {
547				BACKGROUND_DRAW_PIXEL_16_NORMAL;
548			}
549		} else {
550			for (outX = 0; outX < end; ++outX) {
551				BACKGROUND_DRAW_PIXEL_16_VARIANT;
552			}
553		}
554
555		tileX = 30;
556		BACKGROUND_TEXT_SELECT_CHARACTER;
557		charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
558		tileData = ((uint32_t*)renderer->d.vram)[charBase];
559		if (!variant) {
560			for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
561				BACKGROUND_DRAW_PIXEL_16_NORMAL;
562			}
563		} else {
564			for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
565				BACKGROUND_DRAW_PIXEL_16_VARIANT;
566			}
567		}
568
569		tileX = 1;
570		outX = end;
571	}
572
573	if (!background->multipalette) {
574		if (!variant) {
575			BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
576		 } else {
577			BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
578		 }
579	} else {
580		if (!variant) {
581			BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
582		 } else {
583			BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
584		 }
585	}
586}
587
588static const int _objSizes[32] = {
589	8, 8,
590	16, 16,
591	32, 32,
592	64, 64,
593	16, 8,
594	32, 8,
595	32, 16,
596	64, 32,
597	8, 16,
598	8, 32,
599	16, 32,
600	32, 64,
601	0, 0,
602	0, 0,
603	0, 0,
604	0, 0
605};
606
607#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
608		SPRITE_YBASE_ ## DEPTH(inY); \
609		for (int outX = x >= start ? x : start; outX < x + width && outX < end; ++outX) { \
610			int inX = outX - x; \
611			if (sprite->hflip) { \
612				inX = width - inX - 1; \
613			} \
614			if (renderer->flags[outX].isSprite) { \
615				continue; \
616			} \
617			SPRITE_XBASE_ ## DEPTH(inX); \
618			SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
619		}
620
621#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
622	for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
623		if (renderer->flags[outX].isSprite) { \
624			continue; \
625		} \
626		int inY = y - sprite->y; \
627		int inX = outX - x; \
628		int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
629		int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
630		\
631		if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
632			continue; \
633		} \
634		\
635		SPRITE_YBASE_ ## DEPTH(localY); \
636		SPRITE_XBASE_ ## DEPTH(localX); \
637		SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
638	}
639
640#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
641#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
642
643#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
644	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
645	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
646	if (tileData) { \
647		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)]; \
648		renderer->flags[outX] = flags; \
649	}
650
651#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
652	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
653	tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
654	if (tileData) { \
655		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)]; \
656		renderer->flags[outX] = flags; \
657	}
658
659#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
660#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x100) + (localY & 0x7) * 8;
661
662#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
663	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
664	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
665	if (tileData) { \
666		renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData]; \
667		renderer->flags[outX] = flags; \
668	}
669
670#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
671	uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
672	tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
673	if (tileData) { \
674		renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData]; \
675		renderer->flags[outX] = flags; \
676	}
677
678static void _drawSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
679	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
680	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
681	int start = renderer->start;
682	int end = renderer->end;
683	if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
684		return;
685	}
686	struct PixelFlags flags = {
687		.priority = sprite->priority,
688		.isSprite = 1,
689		.target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
690		.target2 = renderer->target2Obj
691	};
692	int x = sprite->x;
693	int inY = y - sprite->y;
694	if (sprite->y + height - 256 >= 0) {
695		inY += 256;
696	}
697	if (sprite->vflip) {
698		inY = height - inY - 1;
699	}
700	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
701	int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
702	if (!sprite->multipalette) {
703		if (!variant) {
704			SPRITE_NORMAL_LOOP(16, NORMAL);
705		} else {
706			SPRITE_NORMAL_LOOP(16, VARIANT);
707		}
708	} else {
709		if (!variant) {
710			SPRITE_NORMAL_LOOP(256, NORMAL);
711		} else {
712			SPRITE_NORMAL_LOOP(256, VARIANT);
713		}
714	}
715}
716
717static void _drawTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
718	int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
719	int totalWidth = width << sprite->doublesize;
720	int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
721	int totalHeight = height << sprite->doublesize;
722	int start = renderer->start;
723	int end = renderer->end;
724	if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
725		return;
726	}
727	struct PixelFlags flags = {
728		.priority = sprite->priority,
729		.isSprite = 1,
730		.target1 = (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT,
731		.target2 = renderer->target2Obj
732	};
733	int x = sprite->x;
734	unsigned charBase = BASE_TILE + sprite->tile * 0x20;
735	struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
736	int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
737	if (!sprite->multipalette) {
738		if (!variant) {
739			SPRITE_TRANSFORMED_LOOP(16, NORMAL);
740		} else {
741			SPRITE_TRANSFORMED_LOOP(16, VARIANT);
742		}
743	} else {
744		if (!variant) {
745			SPRITE_TRANSFORMED_LOOP(256, NORMAL);
746		} else {
747			SPRITE_TRANSFORMED_LOOP(256, VARIANT);
748		}
749	}
750}
751
752static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
753	if (renderer->blendEffect == BLEND_BRIGHTEN) {
754		for (int i = 0; i < 512; ++i) {
755			renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
756		}
757	} else if (renderer->blendEffect == BLEND_DARKEN) {
758		for (int i = 0; i < 512; ++i) {
759			renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
760		}
761	} else {
762		for (int i = 0; i < 512; ++i) {
763			renderer->variantPalette[i] = renderer->normalPalette[i];
764		}
765	}
766}
767
768static inline uint32_t _brighten(uint32_t color, int y) {
769	uint32_t c = 0;
770	uint32_t a;
771	a = color & 0xF8;
772	c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
773
774	a = color & 0xF800;
775	c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
776
777	a = color & 0xF80000;
778	c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
779	return c;
780}
781
782static inline uint32_t _darken(uint32_t color, int y) {
783	uint32_t c = 0;
784	uint32_t a;
785	a = color & 0xF8;
786	c |= (a - (a * y) / 16) & 0xF8;
787
788	a = color & 0xF800;
789	c |= (a - (a * y) / 16) & 0xF800;
790
791	a = color & 0xF80000;
792	c |= (a - (a * y) / 16) & 0xF80000;
793	return c;
794}
795
796static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
797	uint32_t c = 0;
798	uint32_t a, b;
799	a = colorA & 0xF8;
800	b = colorB & 0xF8;
801	c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
802	if (c & 0x00000100) {
803		c = 0x000000F8;
804	}
805
806	a = colorA & 0xF800;
807	b = colorB & 0xF800;
808	c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
809	if (c & 0x00010000) {
810		c = (c & 0x000000F8) | 0x0000F800;
811	}
812
813	a = colorA & 0xF80000;
814	b = colorB & 0xF80000;
815	c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
816	if (c & 0x01000000) {
817		c = (c & 0x0000F8F8) | 0x00F80000;
818	}
819	return c;
820}