all repos — mgba @ 3a8c5c4bf16e87f115aa7d960c53ba2f20cb1155

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