all repos — mgba @ e7be40e80ccb6dbeee0b79c97f349859b1b4afb8

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	localX = tileX * 8 + inX; \
477	for (; tileX < tileEnd; ++tileX) { \
478		mapData = background->mapCache[(localX >> 3) & 0x3F]; \
479		localX += 8; \
480		localY = inY & 0x7; \
481		if (GBA_TEXT_MAP_VFLIP(mapData)) { \
482			localY = 7 - localY; \
483		} \
484		charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
485		vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
486		tileData = carryData; \
487		for (x = 0; x < 8; ++x) { \
488			if (!mosaicWait) { \
489				paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
490				palette = &mainPalette[paletteData]; \
491				if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
492					if (x >= 4) { \
493						LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
494						tileData >>= (x - 4) * 8; \
495					} else { \
496						LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
497						tileData >>= x * 8; \
498					} \
499				} else { \
500					if (x >= 4) { \
501						LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
502						tileData >>= (7 - x) * 8; \
503					} else { \
504						LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
505						tileData >>= (3 - x) * 8; \
506					} \
507				} \
508				tileData &= 0xFF; \
509				carryData = tileData; \
510				mosaicWait = mosaicH; \
511			} \
512			tileData |= tileData << 8; \
513			--mosaicWait; \
514			BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
515			++outX; \
516		} \
517	}
518
519#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
520	if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
521		int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
522		int x; \
523		int mosaicWait = (mosaicH - outX + renderer->masterEnd * mosaicH) % mosaicH; \
524		int carryData = 0; \
525		paletteData = 0; /* Quiets compiler warning */ \
526		DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
527		return; \
528	} \
529	\
530	if (inX & 0x7) { \
531		localX = tileX * 8 + inX; \
532		BACKGROUND_TEXT_SELECT_CHARACTER; \
533		localY = inY & 0x7; \
534		if (GBA_TEXT_MAP_VFLIP(mapData)) { \
535			localY = 7 - localY; \
536		} \
537		int mod8 = inX & 0x7; \
538		int end = outX + 0x8 - mod8; \
539		if (end > renderer->end) { \
540			end = renderer->end; \
541		} \
542		if (UNLIKELY(end == outX)) { \
543			return; \
544		} \
545		if (UNLIKELY(end < outX)) { \
546			mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw!"); \
547			return; \
548		} \
549		DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
550		outX = end; \
551		if (tileX < tileEnd) { \
552			++tileX; \
553		} else if (VIDEO_CHECKS && UNLIKELY(tileX > tileEnd)) { \
554			mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw! tileX (%u) > tileEnd (%u)", tileX, tileEnd); \
555			return; \
556		} \
557		length -= end - renderer->start; \
558	} \
559	localX = (tileX * 8 + inX) & 0x1FF; \
560	DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \
561	if (length & 0x7) { \
562		localX = tileX * 8 + inX; \
563		BACKGROUND_TEXT_SELECT_CHARACTER; \
564		localY = inY & 0x7; \
565		if (GBA_TEXT_MAP_VFLIP(mapData)) { \
566			localY = 7 - localY; \
567		} \
568		int mod8 = length & 0x7; \
569		if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \
570			mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw!"); \
571			return; \
572		} \
573		DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
574	} \
575	if (VIDEO_CHECKS && UNLIKELY(outX > renderer->masterEnd)) { \
576		mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw occurred!"); \
577		return; \
578	}
579
580void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
581	int inX = (renderer->start + background->x - background->offsetX) & 0x1FF;
582	int length = renderer->end - renderer->start;
583	if (background->mosaic) {
584		int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
585		y -= y % mosaicV;
586	}
587	int inY = y + background->y - background->offsetY;
588	uint16_t mapData;
589
590	unsigned yBase = inY & 0xF8;
591	if (background->size == 2) {
592		yBase += inY & 0x100;
593	} else if (background->size == 3) {
594		yBase += (inY & 0x100) << 1;
595	}
596	yBase <<= 3;
597
598	int localX;
599	int localY;
600
601	unsigned xBase;
602
603	uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
604	flags |= FLAG_TARGET_2 * background->target2;
605	int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed));
606	objwinFlags |= flags;
607	flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));
608	if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) {
609		flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
610		objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
611	}
612
613	uint32_t screenBase;
614	uint32_t charBase;
615	int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
616	color_t* mainPalette;
617	if (background->multipalette && background->extPalette) {
618		mainPalette = background->extPalette;
619		if (variant) {
620			mainPalette = background->variantPalette;
621		}
622	} else {
623		mainPalette = renderer->normalPalette;
624		if (variant) {
625			mainPalette = renderer->variantPalette;
626		}
627	}
628	color_t* palette = mainPalette;
629	PREPARE_OBJWIN;
630
631	int outX = renderer->start;
632
633	uint32_t tileData;
634	uint32_t current;
635	int pixelData;
636	int paletteData;
637	int tileX;
638	int tileEnd = ((length + inX) >> 3) - (inX >> 3);
639	uint16_t* vram = NULL;
640
641	if (background->yCache != inY >> 3) {
642		localX = 0;
643		for (tileX = 0; tileX < 64; ++tileX, localX += 8) {
644			BACKGROUND_TEXT_SELECT_CHARACTER;
645			background->mapCache[tileX] = mapData;
646		}
647		background->yCache = inY >> 3;
648	}
649
650	tileX = 0;
651	if (!objwinSlowPath) {
652		if (!(flags & FLAG_TARGET_2)) {
653			if (!background->multipalette) {
654				DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
655			} else if (!background->extPalette) {
656				DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
657			} else {
658				DRAW_BACKGROUND_MODE_0(256EXT, NoBlend, NO_OBJWIN);
659			}
660		} else {
661			if (!background->multipalette) {
662				DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
663			} else if (!background->extPalette) {
664				DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
665			} else {
666				DRAW_BACKGROUND_MODE_0(256EXT, Blend, NO_OBJWIN);
667			}
668		}
669	} else {
670		if (!(flags & FLAG_TARGET_2)) {
671			if (!background->multipalette) {
672				DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
673			} else if (!background->extPalette) {
674				DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
675			} else {
676				DRAW_BACKGROUND_MODE_0(256EXT, NoBlend, OBJWIN);
677			}
678		} else {
679			if (!background->multipalette) {
680				DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
681			} else if (!background->extPalette) {
682				DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
683			} else {
684				DRAW_BACKGROUND_MODE_0(256EXT, Blend, OBJWIN);
685			}
686		}
687	}
688}