all repos — mgba @ 9bef681c7d15ab97afdc306709ea2160ab649d82

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