all repos — mgba @ cf5d6709fed95b84a52cf172367df7b7dd84ed9f

mGBA Game Boy Advance Emulator

src/gba/renderers/software-mode0.c (view raw)

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