all repos — mgba @ 17759173f2898b1caf4259fa5e4b7e46ecd6f3c2

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