all repos — mgba @ 197f73cf85d8b81da9c9620881991543e9d77b5f

mGBA Game Boy Advance Emulator

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

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "gba/renderers/software-private.h"
  7
  8#include <mgba/internal/gba/gba.h>
  9
 10#define BACKGROUND_TEXT_SELECT_CHARACTER \
 11	localX = tileX * 8 + inX; \
 12	xBase = localX & 0xF8; \
 13	if (background->size & 1) { \
 14		xBase += (localX & 0x100) << 5; \
 15	} \
 16	screenBase = background->screenBase + yBase + (xBase >> 2); \
 17	uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
 18	if (UNLIKELY(!screenBlock)) { \
 19		return; \
 20	} \
 21	LOAD_16(mapData, screenBase & VRAM_BLOCK_MASK, screenBlock); \
 22	localY = inY & 0x7; \
 23	if (GBA_TEXT_MAP_VFLIP(mapData)) { \
 24		localY = 7 - localY; \
 25	}
 26
 27#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
 28	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 29	palette = &mainPalette[paletteData]; \
 30	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 31	vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
 32	if (LIKELY(vram)) { \
 33		LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
 34		if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 35			tileData >>= 4 * mod8; \
 36			for (; outX < end; ++outX) { \
 37				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
 38			} \
 39		} else { \
 40			for (outX = end - 1; outX >= renderer->start; --outX) { \
 41				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
 42			} \
 43			outX = end; \
 44		} \
 45	}
 46
 47#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
 48	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 49	vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
 50	if (UNLIKELY(!vram)) { \
 51		return; \
 52	} \
 53	LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
 54	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 55	palette = &mainPalette[paletteData]; \
 56	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
 57		if (outX < renderer->start) { \
 58			tileData >>= 4 * (renderer->start - outX); \
 59			outX = renderer->start; \
 60		} \
 61		for (; outX < renderer->end; ++outX) { \
 62			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
 63		} \
 64	} else { \
 65		tileData >>= 4 * (0x8 - mod8); \
 66		int end = renderer->end - 8; \
 67		if (end < -1) { \
 68			end = -1; \
 69		} \
 70		outX = renderer->end - 1; \
 71		for (; outX > end; --outX) { \
 72			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
 73		} \
 74		/* Needed for consistency checks */ \
 75		if (VIDEO_CHECKS) { \
 76			outX = renderer->end; \
 77		} \
 78	}
 79
 80#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
 81	x = inX & 7; \
 82	if (mosaicWait) { \
 83		int baseX = x - (mosaicH - mosaicWait); \
 84		if (baseX < 0) { \
 85			int disturbX = (16 + baseX) >> 3; \
 86			inX -= disturbX << 3; \
 87			BACKGROUND_TEXT_SELECT_CHARACTER; \
 88			baseX -= disturbX << 3; \
 89			inX += disturbX << 3; \
 90		} else { \
 91			BACKGROUND_TEXT_SELECT_CHARACTER; \
 92		} \
 93		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
 94		vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
 95		if (UNLIKELY(!vram)) { \
 96			carryData = 0; \
 97		} else { \
 98			paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
 99			palette = &mainPalette[paletteData]; \
100			LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
101			if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
102				tileData >>= 4 * baseX; \
103			} else { \
104				tileData >>= 4 * (7 - baseX); \
105			} \
106			tileData &= 0xF; \
107			tileData |= tileData << 4; \
108			tileData |= tileData << 8; \
109			tileData |= tileData << 16; \
110			carryData = tileData; \
111		} \
112	} \
113	for (; length; ++tileX) { \
114		BACKGROUND_TEXT_SELECT_CHARACTER; \
115		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
116		vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
117		tileData = carryData; \
118		for (; x < 8 && length; ++x, --length) { \
119			if (!mosaicWait) { \
120				if (UNLIKELY(!vram)) { \
121					carryData = 0; \
122				} else { \
123					paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
124					palette = &mainPalette[paletteData]; \
125					LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
126					if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
127						tileData >>= x * 4; \
128					} else { \
129						tileData >>= (7 - x) * 4; \
130					} \
131					tileData &= 0xF; \
132					tileData |= tileData << 4; \
133					tileData |= tileData << 8; \
134					tileData |= tileData << 16; \
135					carryData = tileData; \
136				} \
137				mosaicWait = mosaicH; \
138			} \
139			--mosaicWait; \
140			BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
141			++outX; \
142		} \
143		x = 0; \
144	}
145
146#define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \
147	for (; tileX < tileEnd; ++tileX) { \
148		BACKGROUND_TEXT_SELECT_CHARACTER; \
149		paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
150		palette = &mainPalette[paletteData]; \
151		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
152		vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
153		if (UNLIKELY(!vram)) { \
154			outX += 8; \
155			continue; \
156		} \
157		LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
158		if (tileData) { \
159			if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
160				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
161				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
162				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
163				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
164				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
165				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
166				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
167				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
168			} else { \
169				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
170				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
171				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
172				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
173				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
174				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
175				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
176				BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
177			} \
178		} \
179		outX += 8; \
180	}
181
182#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
183	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
184	vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
185	int end2 = end - 4; \
186	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
187		int shift = inX & 0x3; \
188		if (LIKELY(vram)) { \
189			if (end2 > outX) { \
190				LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
191				tileData >>= 8 * shift; \
192				shift = 0; \
193				for (; outX < end2; ++outX) { \
194					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
195				} \
196			} \
197		} \
198		\
199		if (LIKELY(vram)) { \
200			LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
201			tileData >>= 8 * shift; \
202			for (; outX < end; ++outX) { \
203				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
204			} \
205		} \
206	} else { \
207		int start = outX; \
208		outX = end - 1; \
209		if (LIKELY(vram)) { \
210			if (end2 > start) { \
211				LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
212				for (; outX >= end2; --outX) { \
213					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
214				} \
215				charBase += 4; \
216			} \
217		} \
218		\
219		if (LIKELY(vram)) { \
220			LOAD_32(tileData, charBase, vram); \
221			for (; outX >= renderer->start; --outX) { \
222				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
223			} \
224		} \
225		outX = end; \
226	}
227
228#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
229	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
230	vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
231	if (UNLIKELY(!vram)) { \
232		return; \
233	} \
234	int end = mod8 - 4; \
235	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
236		if (end > 0) { \
237			LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
238			for (; outX < renderer->end - end; ++outX) { \
239				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
240			} \
241			charBase += 4; \
242		} \
243		\
244		LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
245		for (; outX < renderer->end; ++outX) { \
246			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
247		} \
248	} else { \
249		int shift = (8 - mod8) & 0x3; \
250		int start = outX; \
251		outX = renderer->end - 1; \
252		if (end > 0) { \
253			LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
254			tileData >>= 8 * shift; \
255			for (; outX >= start + 4; --outX) { \
256				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
257			} \
258			shift = 0; \
259		} \
260		\
261		LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
262		tileData >>= 8 * shift; \
263		for (; outX >= start; --outX) { \
264			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
265		} \
266		/* Needed for consistency checks */ \
267		if (VIDEO_CHECKS) { \
268			outX = renderer->end; \
269		} \
270	}
271
272#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
273	for (; tileX < tileEnd; ++tileX) { \
274		BACKGROUND_TEXT_SELECT_CHARACTER; \
275		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
276		vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
277		if (UNLIKELY(!vram)) { \
278			outX += 8; \
279			continue; \
280		} \
281		if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
282			LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
283			if (tileData) { \
284					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
285					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
286					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
287					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
288			} \
289			outX += 4; \
290			LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
291			if (tileData) { \
292					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
293					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
294					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
295					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
296			} \
297			outX += 4; \
298		} else { \
299			LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
300			if (tileData) { \
301				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
302				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
303				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
304				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
305			} \
306			outX += 4; \
307			LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
308			if (tileData) { \
309				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
310				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
311				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
312				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
313			} \
314			outX += 4; \
315		} \
316	}
317
318#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
319	for (; tileX < tileEnd; ++tileX) { \
320		BACKGROUND_TEXT_SELECT_CHARACTER; \
321		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
322		vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
323		tileData = carryData; \
324		for (x = 0; x < 8; ++x) { \
325			if (!mosaicWait) { \
326				if (UNLIKELY(!vram)) { \
327					carryData = 0; \
328				} else { \
329					if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
330						if (x >= 4) { \
331							LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
332							tileData >>= (x - 4) * 8; \
333						} else { \
334							LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
335							tileData >>= x * 8; \
336						} \
337					} else { \
338						if (x >= 4) { \
339							LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
340							tileData >>= (7 - x) * 8; \
341						} else { \
342							LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
343							tileData >>= (3 - x) * 8; \
344						} \
345					} \
346					tileData &= 0xFF; \
347					carryData = tileData; \
348				} \
349				mosaicWait = mosaicH; \
350			} \
351			tileData |= tileData << 8; \
352			--mosaicWait; \
353			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
354			++outX; \
355		} \
356	}
357
358#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256EXT(BLEND, OBJWIN) \
359	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
360	palette = &mainPalette[paletteData]; \
361	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
362	vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
363	int end2 = end - 4; \
364	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
365		int shift = inX & 0x3; \
366		if (LIKELY(vram)) { \
367			if (end2 > outX) { \
368				LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
369				tileData >>= 8 * shift; \
370				shift = 0; \
371				for (; outX < end2; ++outX) { \
372					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
373				} \
374			} \
375		} \
376		\
377		if (LIKELY(vram)) { \
378			LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
379			tileData >>= 8 * shift; \
380			for (; outX < end; ++outX) { \
381				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
382			} \
383		} \
384	} else { \
385		int start = outX; \
386		outX = end - 1; \
387		if (LIKELY(vram)) { \
388			if (end2 > start) { \
389				LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
390				for (; outX >= end2; --outX) { \
391					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
392				} \
393				charBase += 4; \
394			} \
395		} \
396		\
397		if (LIKELY(vram)) { \
398			LOAD_32(tileData, charBase, vram); \
399			for (; outX >= renderer->start; --outX) { \
400				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
401			} \
402		} \
403		outX = end; \
404	}
405
406#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256EXT(BLEND, OBJWIN) \
407	charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
408	vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
409	if (UNLIKELY(!vram)) { \
410		return; \
411	} \
412	paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
413	palette = &mainPalette[paletteData]; \
414	int end = mod8 - 4; \
415	if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
416		if (end > 0) { \
417			LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
418			for (; outX < renderer->end - end; ++outX) { \
419				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
420			} \
421			charBase += 4; \
422		} \
423		\
424		LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
425		for (; outX < renderer->end; ++outX) { \
426			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
427		} \
428	} else { \
429		int shift = (8 - mod8) & 0x3; \
430		int start = outX; \
431		outX = renderer->end - 1; \
432		if (end > 0) { \
433			LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
434			tileData >>= 8 * shift; \
435			for (; outX >= start + 4; --outX) { \
436				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
437			} \
438			shift = 0; \
439		} \
440		\
441		LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
442		tileData >>= 8 * shift; \
443		for (; outX >= start; --outX) { \
444			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
445		} \
446		/* Needed for consistency checks */ \
447		if (VIDEO_CHECKS) { \
448			outX = renderer->end; \
449		} \
450	}
451
452#define DRAW_BACKGROUND_MODE_0_TILES_256EXT(BLEND, OBJWIN) \
453	for (; tileX < tileEnd; ++tileX) { \
454		BACKGROUND_TEXT_SELECT_CHARACTER; \
455		paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
456		palette = &mainPalette[paletteData]; \
457		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
458		vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
459		if (UNLIKELY(!vram)) { \
460			outX += 8; \
461			continue; \
462		} \
463		if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
464			LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
465			if (tileData) { \
466					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
467					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
468					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
469					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
470			} \
471			outX += 4; \
472			LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
473			if (tileData) { \
474					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
475					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
476					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
477					BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
478			} \
479			outX += 4; \
480		} else { \
481			LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
482			if (tileData) { \
483				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
484				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
485				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
486				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
487			} \
488			outX += 4; \
489			LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
490			if (tileData) { \
491				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
492				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
493				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
494				BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
495			} \
496			outX += 4; \
497		} \
498	}
499
500#define DRAW_BACKGROUND_MODE_0_MOSAIC_256EXT(BLEND, OBJWIN) \
501	for (; tileX < tileEnd; ++tileX) { \
502		BACKGROUND_TEXT_SELECT_CHARACTER; \
503		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
504		vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
505		tileData = carryData; \
506		for (x = 0; x < 8; ++x) { \
507			if (!mosaicWait) { \
508				if (UNLIKELY(!vram)) { \
509					carryData = 0; \
510				} else { \
511					paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
512					palette = &mainPalette[paletteData]; \
513					if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
514						if (x >= 4) { \
515							LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
516							tileData >>= (x - 4) * 8; \
517						} else { \
518							LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
519							tileData >>= x * 8; \
520						} \
521					} else { \
522						if (x >= 4) { \
523							LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
524							tileData >>= (7 - x) * 8; \
525						} else { \
526							LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
527							tileData >>= (3 - x) * 8; \
528						} \
529					} \
530					tileData &= 0xFF; \
531					carryData = tileData; \
532				} \
533				mosaicWait = mosaicH; \
534			} \
535			tileData |= tileData << 8; \
536			--mosaicWait; \
537			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
538			++outX; \
539		} \
540	}
541
542#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
543	if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
544		int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
545		int x; \
546		int mosaicWait = (mosaicH - outX + renderer->masterEnd * mosaicH) % mosaicH; \
547		int carryData = 0; \
548		paletteData = 0; /* Quiets compiler warning */ \
549		DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
550		return; \
551	} \
552	\
553	if (inX & 0x7) { \
554		BACKGROUND_TEXT_SELECT_CHARACTER; \
555		\
556		int mod8 = inX & 0x7; \
557		int end = outX + 0x8 - mod8; \
558		if (end > renderer->end) { \
559			end = renderer->end; \
560		} \
561		if (UNLIKELY(end == outX)) { \
562			return; \
563		} \
564		if (UNLIKELY(end < outX)) { \
565			mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw!"); \
566			return; \
567		} \
568		DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
569		outX = end; \
570		if (tileX < tileEnd) { \
571			++tileX; \
572		} else if (VIDEO_CHECKS && UNLIKELY(tileX > tileEnd)) { \
573			mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw! tileX (%u) > tileEnd (%u)", tileX, tileEnd); \
574			return; \
575		} \
576		length -= end - renderer->start; \
577	} \
578	DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \
579	if (length & 0x7) { \
580		BACKGROUND_TEXT_SELECT_CHARACTER; \
581		\
582		int mod8 = length & 0x7; \
583		if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \
584			mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw!"); \
585			return; \
586		} \
587		DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
588	} \
589	if (VIDEO_CHECKS && UNLIKELY(outX > renderer->masterEnd)) { \
590		mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw occurred!"); \
591		return; \
592	}
593
594void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
595	int inX = renderer->start + background->x;
596	int length = renderer->end - renderer->start;
597	if (background->mosaic) {
598		int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
599		y -= y % mosaicV;
600	}
601	int inY = y + background->y;
602	uint16_t mapData;
603
604	unsigned yBase = inY & 0xF8;
605	if (background->size == 2) {
606		yBase += inY & 0x100;
607	} else if (background->size == 3) {
608		yBase += (inY & 0x100) << 1;
609	}
610	yBase <<= 3;
611
612	int localX;
613	int localY;
614
615	unsigned xBase;
616
617	uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
618	flags |= FLAG_TARGET_2 * background->target2;
619	int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed));
620	objwinFlags |= flags;
621	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));
622	if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) {
623		flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
624		objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
625	}
626
627	uint32_t screenBase;
628	uint32_t charBase;
629	int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
630	color_t* mainPalette;
631	if (background->multipalette && background->extPalette) {
632		mainPalette = background->extPalette;
633		if (variant) {
634			mainPalette = background->variantPalette;
635		}
636	} else {
637		mainPalette = renderer->normalPalette;
638		if (variant) {
639			mainPalette = renderer->variantPalette;
640		}
641	}
642	color_t* palette = mainPalette;
643	PREPARE_OBJWIN;
644
645	int outX = renderer->start;
646
647	uint32_t tileData;
648	uint32_t current;
649	int pixelData;
650	int paletteData;
651	int tileX = 0;
652	int tileEnd = ((length + inX) >> 3) - (inX >> 3);
653	uint16_t* vram = NULL;
654
655	if (!objwinSlowPath) {
656		if (!(flags & FLAG_TARGET_2)) {
657			if (!background->multipalette) {
658				DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
659			} else if (!background->extPalette) {
660				DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
661			} else {
662				DRAW_BACKGROUND_MODE_0(256EXT, NoBlend, NO_OBJWIN);
663			}
664		} else {
665			if (!background->multipalette) {
666				DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
667			} else if (!background->extPalette) {
668				DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
669			} else {
670				DRAW_BACKGROUND_MODE_0(256EXT, Blend, NO_OBJWIN);
671			}
672		}
673	} else {
674		if (!(flags & FLAG_TARGET_2)) {
675			if (!background->multipalette) {
676				DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
677			} else if (!background->extPalette) {
678				DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
679			} else {
680				DRAW_BACKGROUND_MODE_0(256EXT, NoBlend, OBJWIN);
681			}
682		} else {
683			if (!background->multipalette) {
684				DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
685			} else if (!background->extPalette) {
686				DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
687			} else {
688				DRAW_BACKGROUND_MODE_0(256EXT, Blend, OBJWIN);
689			}
690		}
691	}
692}