all repos — mgba @ 974c41e8cfbaa31a0b1534f76843f476970647e6

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