all repos — mgba @ 9dfcef3f45efe580e252ec8bc3852257e9de9fd6

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