all repos — mgba @ 63ed7421ce05174608dd1799f8e7b0a5f01d5c3d

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