all repos — mgba @ 3ccffdc29eab88906a6cbb9d686f474261da4e16

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