all repos — mgba @ 813b2c069761773be3e08e39c74e75c25ba6e667

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