all repos — mgba @ 5efacfa097d75f5fcd0672f8b98a1bcc36e4ccbb

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