all repos — mgba @ fa884d071ecaa3e05ff20b45a67bf9500dd3d6b6

mGBA Game Boy Advance Emulator

src/gb/renderers/software.c (view raw)

  1/* Copyright (c) 2013-2016 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 <mgba/internal/gb/renderers/software.h>
  7
  8#include <mgba/core/tile-cache.h>
  9#include <mgba/internal/gb/io.h>
 10#include <mgba-util/memory.h>
 11
 12static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model);
 13static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
 14static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
 15static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
 16static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
 17static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax);
 18static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
 19static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer);
 20static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels);
 21static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels);
 22
 23static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy);
 24static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y);
 25
 26static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
 27	// TODO: Dynamic from dmgPalette
 28#ifdef COLOR_16_BIT
 29#ifdef COLOR_5_6_5
 30	color_t palette0 = 0xFFDF;
 31#else
 32	color_t palette0 = 0x7FFF;
 33#endif
 34#else
 35	color_t palette0 = 0xFFFFFF;
 36#endif
 37
 38	int y;
 39	for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
 40		color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y];
 41		int x;
 42		for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
 43			row[x + 0] = palette0;
 44			row[x + 1] = palette0;
 45			row[x + 2] = palette0;
 46			row[x + 3] = palette0;
 47		}
 48	}
 49}
 50
 51void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
 52	renderer->d.init = GBVideoSoftwareRendererInit;
 53	renderer->d.deinit = GBVideoSoftwareRendererDeinit;
 54	renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister;
 55	renderer->d.writePalette = GBVideoSoftwareRendererWritePalette;
 56	renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM;
 57	renderer->d.drawRange = GBVideoSoftwareRendererDrawRange;
 58	renderer->d.finishScanline = GBVideoSoftwareRendererFinishScanline;
 59	renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame;
 60	renderer->d.getPixels = GBVideoSoftwareRendererGetPixels;
 61	renderer->d.putPixels = GBVideoSoftwareRendererPutPixels;
 62
 63	renderer->temporaryBuffer = 0;
 64}
 65
 66static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
 67	struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
 68	softwareRenderer->lcdc = 0;
 69	softwareRenderer->scy = 0;
 70	softwareRenderer->scx = 0;
 71	softwareRenderer->wy = 0;
 72	softwareRenderer->currentWy = 0;
 73	softwareRenderer->wx = 0;
 74	softwareRenderer->model = model;
 75
 76	_clearScreen(softwareRenderer);
 77}
 78
 79static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {
 80	struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
 81	UNUSED(softwareRenderer);
 82}
 83
 84static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
 85	struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
 86	switch (address) {
 87	case REG_LCDC:
 88		if (GBRegisterLCDCIsEnable(softwareRenderer->lcdc) && !GBRegisterLCDCIsEnable(value)) {
 89			_clearScreen(softwareRenderer);
 90		}
 91		softwareRenderer->lcdc = value;
 92		break;
 93	case REG_SCY:
 94		softwareRenderer->scy = value;
 95		break;
 96	case REG_SCX:
 97		softwareRenderer->scx = value;
 98		break;
 99	case REG_WY:
100		softwareRenderer->wy = value;
101		break;
102	case REG_WX:
103		softwareRenderer->wx = value;
104		break;
105	}
106	return value;
107}
108
109static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) {
110	struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
111#ifdef COLOR_16_BIT
112#ifdef COLOR_5_6_5
113	color_t color = 0;
114	color |= (value & 0x001F) << 11;
115	color |= (value & 0x03E0) << 1;
116	color |= (value & 0x7C00) >> 10;
117#else
118	color_t color = value;
119#endif
120#else
121	color_t color = 0;
122	color |= (value << 3) & 0xF8;
123	color |= (value << 6) & 0xF800;
124	color |= (value << 9) & 0xF80000;
125	color |= (color >> 5) & 0x070707;
126#endif
127	softwareRenderer->palette[index] = color;
128	if (renderer->cache) {
129		mTileCacheWritePalette(renderer->cache, index << 1);
130	}
131}
132
133static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
134	if (renderer->cache) {
135		mTileCacheWriteVRAM(renderer->cache, address);
136	}
137}
138
139static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) {
140	struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
141	uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP];
142	if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) {
143		maps += GB_SIZE_MAP;
144	}
145	if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) {
146		if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && endX >= softwareRenderer->wx - 7) {
147			if (softwareRenderer->wx - 7 > 0) {
148				GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y);
149			}
150
151			maps = &softwareRenderer->d.vram[GB_BASE_MAP];
152			if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) {
153				maps += GB_SIZE_MAP;
154			}
155			GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy);
156		} else {
157			GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y);
158		}
159	} else {
160		memset(&softwareRenderer->row[startX], 0, endX - startX);
161	}
162
163	if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc)) {
164		size_t i;
165		for (i = 0; i < oamMax; ++i) {
166			GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y);
167		}
168	}
169	color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
170	int x;
171	for (x = startX; x + 7 < (endX & ~7); x += 8) {
172		row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F];
173		row[x + 1] = softwareRenderer->palette[softwareRenderer->row[x + 1] & 0x7F];
174		row[x + 2] = softwareRenderer->palette[softwareRenderer->row[x + 2] & 0x7F];
175		row[x + 3] = softwareRenderer->palette[softwareRenderer->row[x + 3] & 0x7F];
176		row[x + 4] = softwareRenderer->palette[softwareRenderer->row[x + 4] & 0x7F];
177		row[x + 5] = softwareRenderer->palette[softwareRenderer->row[x + 5] & 0x7F];
178		row[x + 6] = softwareRenderer->palette[softwareRenderer->row[x + 6] & 0x7F];
179		row[x + 7] = softwareRenderer->palette[softwareRenderer->row[x + 7] & 0x7F];
180	}
181	for (; x < endX; ++x) {
182		row[x] = softwareRenderer->palette[softwareRenderer->row[x] & 0x7F];
183	}
184}
185
186static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
187	struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
188	if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && softwareRenderer->wx - 7 < GB_VIDEO_HORIZONTAL_PIXELS) {
189		++softwareRenderer->currentWy;
190	}
191}
192
193static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) {
194	struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
195
196	if (softwareRenderer->temporaryBuffer) {
197		mappedMemoryFree(softwareRenderer->temporaryBuffer, GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4);
198		softwareRenderer->temporaryBuffer = 0;
199	}
200	softwareRenderer->currentWy = 0;
201}
202
203static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy) {
204	uint8_t* data = renderer->d.vram;
205	uint8_t* attr = &maps[GB_SIZE_VRAM_BANK0];
206	if (!GBRegisterLCDCIsTileData(renderer->lcdc)) {
207		data += 0x1000;
208	}
209	int topY = ((sy >> 3) & 0x1F) * 0x20;
210	int bottomY = sy & 7;
211	if (startX < 0) {
212		startX = 0;
213	}
214	int x;
215	if ((startX + sx) & 7) {
216		int startX2 = startX + 8 - ((startX + sx) & 7);
217		for (x = startX; x < startX2; ++x) {
218			uint8_t* localData = data;
219			int localY = bottomY;
220			int topX = ((x + sx) >> 3) & 0x1F;
221			int bottomX = 7 - ((x + sx) & 7);
222			int bgTile;
223			if (GBRegisterLCDCIsTileData(renderer->lcdc)) {
224				bgTile = maps[topX + topY];
225			} else {
226				bgTile = ((int8_t*) maps)[topX + topY];
227			}
228			int p = 0;
229			if (renderer->model >= GB_MODEL_CGB) {
230				GBObjAttributes attrs = attr[topX + topY];
231				p = GBObjAttributesGetCGBPalette(attrs) * 4;
232				if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) {
233					p |= 0x80;
234				}
235				if (GBObjAttributesIsBank(attrs)) {
236					localData += GB_SIZE_VRAM_BANK0;
237				}
238				if (GBObjAttributesIsYFlip(attrs)) {
239					localY = 7 - bottomY;
240				}
241				if (GBObjAttributesIsXFlip(attrs)) {
242					bottomX = 7 - bottomX;
243				}
244			}
245			uint8_t tileDataLower = localData[(bgTile * 8 + localY) * 2];
246			uint8_t tileDataUpper = localData[(bgTile * 8 + localY) * 2 + 1];
247			tileDataUpper >>= bottomX;
248			tileDataLower >>= bottomX;
249			renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
250		}
251		startX = startX2;
252	}
253	for (x = startX; x < endX; x += 8) {
254		uint8_t* localData = data;
255		int localY = bottomY;
256		int topX = ((x + sx) >> 3) & 0x1F;
257		int bgTile;
258		if (GBRegisterLCDCIsTileData(renderer->lcdc)) {
259			bgTile = maps[topX + topY];
260		} else {
261			bgTile = ((int8_t*) maps)[topX + topY];
262		}
263		int p = 0;
264		if (renderer->model >= GB_MODEL_CGB) {
265			GBObjAttributes attrs = attr[topX + topY];
266			p = GBObjAttributesGetCGBPalette(attrs) * 4;
267			if (GBObjAttributesIsPriority(attrs) && GBRegisterLCDCIsBgEnable(renderer->lcdc)) {
268				p |= 0x80;
269			}
270			if (GBObjAttributesIsBank(attrs)) {
271				localData += GB_SIZE_VRAM_BANK0;
272			}
273			if (GBObjAttributesIsYFlip(attrs)) {
274				localY = 7 - bottomY;
275			}
276			if (GBObjAttributesIsXFlip(attrs)) {
277				uint8_t tileDataLower = localData[(bgTile * 8 + localY) * 2];
278				uint8_t tileDataUpper = localData[(bgTile * 8 + localY) * 2 + 1];
279				renderer->row[x + 0] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
280				renderer->row[x + 1] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
281				renderer->row[x + 2] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
282				renderer->row[x + 3] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
283				renderer->row[x + 4] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
284				renderer->row[x + 5] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
285				renderer->row[x + 6] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
286				renderer->row[x + 7] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
287				continue;
288			}
289		}
290		uint8_t tileDataLower = localData[(bgTile * 8 + localY) * 2];
291		uint8_t tileDataUpper = localData[(bgTile * 8 + localY) * 2 + 1];
292		renderer->row[x + 7] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
293		renderer->row[x + 6] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
294		renderer->row[x + 5] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
295		renderer->row[x + 4] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
296		renderer->row[x + 3] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
297		renderer->row[x + 2] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
298		renderer->row[x + 1] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
299		renderer->row[x + 0] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
300	}
301}
302
303static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y) {
304	int ix = obj->x - 8;
305	if (endX < ix || startX >= ix + 8) {
306		return;
307	}
308	if (obj->x < endX) {
309		endX = obj->x;
310	}
311	if (obj->x - 8 > startX) {
312		startX = obj->x - 8;
313	}
314	if (startX < 0) {
315		startX = 0;
316	}
317	uint8_t* data = renderer->d.vram;
318	int tileOffset = 0;
319	int bottomY;
320	if (GBObjAttributesIsYFlip(obj->attr)) {
321		bottomY = 7 - ((y - obj->y - 16) & 7);
322		if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - obj->y < -8) {
323			++tileOffset;
324		}
325	} else {
326		bottomY = (y - obj->y - 16) & 7;
327		if (GBRegisterLCDCIsObjSize(renderer->lcdc) && y - obj->y >= -8) {
328			++tileOffset;
329		}
330	}
331	if (GBRegisterLCDCIsObjSize(renderer->lcdc) && obj->tile & 1) {
332		--tileOffset;
333	}
334	uint8_t mask = GBObjAttributesIsPriority(obj->attr) ? 0x63 : 0x60;
335	uint8_t mask2 = GBObjAttributesIsPriority(obj->attr) ? 0 : 0x83;
336	int p;
337	if (renderer->model >= GB_MODEL_CGB) {
338		p = (GBObjAttributesGetCGBPalette(obj->attr) + 8) * 4;
339		if (GBObjAttributesIsBank(obj->attr)) {
340			data += GB_SIZE_VRAM_BANK0;
341		}
342		if (!GBRegisterLCDCIsBgEnable(renderer->lcdc)) {
343			mask = 0x60;
344			mask2 = 0x83;
345		}
346	} else {
347		p = (GBObjAttributesGetPalette(obj->attr) + 8) * 4;
348	}
349	int bottomX;
350	int x = startX;
351	if ((x - obj->x) & 7) {
352		for (; x < endX; ++x) {
353			if (GBObjAttributesIsXFlip(obj->attr)) {
354				bottomX = (x - obj->x) & 7;
355			} else {
356				bottomX = 7 - ((x - obj->x) & 7);
357			}
358			int objTile = obj->tile + tileOffset;
359			uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
360			uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
361			tileDataUpper >>= bottomX;
362			tileDataLower >>= bottomX;
363			color_t current = renderer->row[x];
364			if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) {
365				renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
366			}
367		}
368	} else if (GBObjAttributesIsXFlip(obj->attr)) {
369		int objTile = obj->tile + tileOffset;
370		uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
371		uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
372		color_t current;
373		current = renderer->row[x];
374		if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) {
375			renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
376		}
377		current = renderer->row[x + 1];
378		if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= 0x80) {
379			renderer->row[x + 1] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
380		}
381		current = renderer->row[x + 2];
382		if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= 0x80) {
383			renderer->row[x + 2] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
384		}
385		current = renderer->row[x + 3];
386		if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= 0x80) {
387			renderer->row[x + 3] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
388		}
389		current = renderer->row[x + 4];
390		if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= 0x80) {
391			renderer->row[x + 4] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
392		}
393		current = renderer->row[x + 5];
394		if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= 0x80) {
395			renderer->row[x + 5] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
396		}
397		current = renderer->row[x + 6];
398		if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= 0x80) {
399			renderer->row[x + 6] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
400		}
401		current = renderer->row[x + 7];
402		if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= 0x80) {
403			renderer->row[x + 7] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
404		}
405	} else {
406		int objTile = obj->tile + tileOffset;
407		uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
408		uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
409		color_t current;
410		current = renderer->row[x + 7];
411		if (((tileDataUpper | tileDataLower) & 1) && !(current & mask) && (current & mask2) <= 0x80) {
412			renderer->row[x + 7] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
413		}
414		current = renderer->row[x + 6];
415		if (((tileDataUpper | tileDataLower) & 2) && !(current & mask) && (current & mask2) <= 0x80) {
416			renderer->row[x + 6] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
417		}
418		current = renderer->row[x + 5];
419		if (((tileDataUpper | tileDataLower) & 4) && !(current & mask) && (current & mask2) <= 0x80) {
420			renderer->row[x + 5] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
421		}
422		current = renderer->row[x + 4];
423		if (((tileDataUpper | tileDataLower) & 8) && !(current & mask) && (current & mask2) <= 0x80) {
424			renderer->row[x + 4] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
425		}
426		current = renderer->row[x + 3];
427		if (((tileDataUpper | tileDataLower) & 16) && !(current & mask) && (current & mask2) <= 0x80) {
428			renderer->row[x + 3] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
429		}
430		current = renderer->row[x + 2];
431		if (((tileDataUpper | tileDataLower) & 32) && !(current & mask) && (current & mask2) <= 0x80) {
432			renderer->row[x + 2] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
433		}
434		current = renderer->row[x + 1];
435		if (((tileDataUpper | tileDataLower) & 64) && !(current & mask) && (current & mask2) <= 0x80) {
436			renderer->row[x + 1] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
437		}
438		current = renderer->row[x];
439		if (((tileDataUpper | tileDataLower) & 128) && !(current & mask) && (current & mask2) <= 0x80) {
440			renderer->row[x] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
441		}
442	}
443}
444
445static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {
446	struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
447	// TODO: Share with GBAVideoSoftwareRendererGetPixels
448#ifdef COLOR_16_BIT
449	*stride = GB_VIDEO_HORIZONTAL_PIXELS;
450	if (!softwareRenderer->temporaryBuffer) {
451		softwareRenderer->temporaryBuffer = anonymousMemoryMap(GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4);
452	}
453	*pixels = softwareRenderer->temporaryBuffer;
454	unsigned y, x;
455	for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
456		for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; ++x) {
457			color_t inColor = softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y + x];
458			uint32_t outColor;
459#ifdef COLOR_5_6_5
460			outColor = (inColor & 0x1F) << 19;
461			outColor |= (inColor & 0x7C0) << 5;
462			outColor |= (inColor & 0xF800) >> 8;
463#else
464			outColor = (inColor & 0x1F) << 3;
465			outColor |= (inColor & 0x3E0) << 6;
466			outColor |= (inColor & 0x7C00) << 9;
467#endif
468			softwareRenderer->temporaryBuffer[GB_VIDEO_HORIZONTAL_PIXELS * y + x] = outColor;
469		}
470	}
471#else
472	*stride = softwareRenderer->outputBufferStride;
473	*pixels = softwareRenderer->outputBuffer;
474#endif
475}
476
477
478static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) {
479	struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
480	// TODO: Share with GBAVideoSoftwareRendererGetPixels
481
482	const color_t* colorPixels = pixels;
483	unsigned i;
484	for (i = 0; i < GB_VIDEO_VERTICAL_PIXELS; ++i) {
485		memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], GB_VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
486	}
487}