all repos — mgba @ e0518fdf30601b2424ab98e7b351cb75d66bcd74

mGBA Game Boy Advance Emulator

src/ds/video.c (view raw)

  1/* Copyright (c) 2013-2015 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/ds/video.h>
  7
  8#include <mgba/core/sync.h>
  9#include <mgba/internal/ds/ds.h>
 10#include <mgba/internal/ds/memory.h>
 11#include <mgba/internal/gba/video.h>
 12
 13#include <mgba-util/memory.h>
 14
 15mLOG_DEFINE_CATEGORY(DS_VIDEO, "DS Video");
 16
 17static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer);
 18static void DSVideoDummyRendererReset(struct DSVideoRenderer* renderer);
 19static void DSVideoDummyRendererDeinit(struct DSVideoRenderer* renderer);
 20static uint16_t DSVideoDummyRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
 21static void DSVideoDummyRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
 22static void DSVideoDummyRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
 23static void DSVideoDummyRendererFinishFrame(struct DSVideoRenderer* renderer);
 24static void DSVideoDummyRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
 25static void DSVideoDummyRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
 26
 27static void _startHblank7(struct mTiming*, void* context, uint32_t cyclesLate);
 28static void _startHdraw7(struct mTiming*, void* context, uint32_t cyclesLate);
 29static void _startHblank9(struct mTiming*, void* context, uint32_t cyclesLate);
 30static void _startHdraw9(struct mTiming*, void* context, uint32_t cyclesLate);
 31
 32static const uint32_t _vramSize[9] = {
 33	0x20000,
 34	0x20000,
 35	0x20000,
 36	0x20000,
 37	0x10000,
 38	0x04000,
 39	0x04000,
 40	0x08000,
 41	0x04000
 42};
 43
 44const struct DSVRAMBankInfo {
 45	int base;
 46	uint32_t mirrorSize;
 47	int mode;
 48	int offset[4];
 49} _vramInfo[9][8] = {
 50	{ // A
 51		{ 0x000, 0x40, 4 }, // LCDC
 52		{ 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG
 53		{ 0x000, 0x10, 2, { 0x00, 0x08, 0x80, 0x80 } }, // A-OBJ
 54	},
 55	{ // B
 56		{ 0x008, 0x40, 4 }, // LCDC
 57		{ 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG
 58		{ 0x000, 0x10, 2, { 0x00, 0x08, 0x80, 0x80 } }, // A-OBJ
 59	},
 60	{ // C
 61		{ 0x010, 0x40, 4 }, // LCDC
 62		{ 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG
 63		{},
 64		{},
 65		{ 0x000, 0x08, 1 }, // B-BG
 66	},
 67	{ // D
 68		{ 0x018, 0x40, 4 }, // LCDC
 69		{ 0x000, 0x20, 0, { 0x00, 0x08, 0x10, 0x18 } }, // A-BG
 70		{},
 71		{},
 72		{ 0x000, 0x08, 3 }, // B-OBJ
 73	},
 74	{ // E
 75		{ 0x020, 0x40, 4 }, // LCDC
 76		{ 0x000, 0x20, 0 }, // A-BG
 77		{ 0x000, 0x10, 2 }, // A-OBJ
 78	},
 79	{ // F
 80		{ 0x024, 0x40, 4 }, // LCDC
 81		{ 0x000, 0x20, 0, { 0x00, 0x01, 0x04, 0x05 } }, // A-BG
 82		{ 0x000, 0x10, 2, { 0x00, 0x01, 0x04, 0x05 } }, // A-OBJ
 83	},
 84	{ // G
 85		{ 0x025, 0x40, 4 }, // LCDC
 86		{ 0x000, 0x20, 0 }, // A-BG
 87		{ 0x000, 0x10, 2 }, // A-OBJ
 88	},
 89	{ // H
 90		{ 0x026, 0x40, 4 }, // LCDC
 91		{ 0x000, 0x04, 1 }, // B-BG
 92		{ 0x000, 0x10, 2 }, // A-OBJ
 93	},
 94	{ // I
 95		{ 0x028, 0x40, 4 }, // LCDC
 96		{ 0x002, 0x04, 1 }, // B-BG
 97		{ 0x000, 0x01, 3 }, // B-OBJ
 98	},
 99};
100
101static struct DSVideoRenderer dummyRenderer = {
102	.init = DSVideoDummyRendererInit,
103	.reset = DSVideoDummyRendererReset,
104	.deinit = DSVideoDummyRendererDeinit,
105	.writeVideoRegister = DSVideoDummyRendererWriteVideoRegister,
106	.writePalette = DSVideoDummyRendererWritePalette,
107	.drawScanline = DSVideoDummyRendererDrawScanline,
108	.finishFrame = DSVideoDummyRendererFinishFrame,
109	.getPixels = DSVideoDummyRendererGetPixels,
110	.putPixels = DSVideoDummyRendererPutPixels,
111};
112
113void DSVideoInit(struct DSVideo* video) {
114	video->renderer = &dummyRenderer;
115	video->vram = NULL;
116	video->frameskip = 0;
117	video->event7.name = "DS7 Video";
118	video->event7.callback = NULL;
119	video->event7.context = video;
120	video->event7.priority = 8;
121	video->event9.name = "DS9 Video";
122	video->event9.callback = NULL;
123	video->event9.context = video;
124	video->event9.priority = 8;
125}
126
127void DSVideoReset(struct DSVideo* video) {
128	video->vcount = 0;
129	video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
130	video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
131
132	video->event7.callback = _startHblank7;
133	video->event9.callback = _startHblank9;
134	mTimingSchedule(&video->p->ds7.timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH);
135	mTimingSchedule(&video->p->ds9.timing, &video->event9, (DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH) * 2);
136
137	video->frameCounter = 0;
138	video->frameskipCounter = 0;
139
140	if (video->vram) {
141		mappedMemoryFree(video->vram, DS_SIZE_VRAM);
142	}
143	video->vram = anonymousMemoryMap(DS_SIZE_VRAM);
144	video->renderer->vram = video->vram;
145
146	video->p->memory.vramBank[0] = &video->vram[0x00000];
147	video->p->memory.vramBank[1] = &video->vram[0x10000];
148	video->p->memory.vramBank[2] = &video->vram[0x20000];
149	video->p->memory.vramBank[3] = &video->vram[0x30000];
150	video->p->memory.vramBank[4] = &video->vram[0x40000];
151	video->p->memory.vramBank[5] = &video->vram[0x48000];
152	video->p->memory.vramBank[6] = &video->vram[0x4A000];
153	video->p->memory.vramBank[7] = &video->vram[0x4C000];
154	video->p->memory.vramBank[8] = &video->vram[0x50000];
155
156	video->renderer->deinit(video->renderer);
157	video->renderer->init(video->renderer);
158}
159
160void DSVideoAssociateRenderer(struct DSVideo* video, struct DSVideoRenderer* renderer) {
161	video->renderer->deinit(video->renderer);
162	video->renderer = renderer;
163	renderer->palette = video->palette;
164	renderer->vram = video->vram;
165	memcpy(renderer->vramABG, video->vramABG, sizeof(renderer->vramABG));
166	memcpy(renderer->vramAOBJ, video->vramAOBJ, sizeof(renderer->vramAOBJ));
167	memcpy(renderer->vramBBG, video->vramBBG, sizeof(renderer->vramBBG));
168	memcpy(renderer->vramBOBJ, video->vramBOBJ, sizeof(renderer->vramBOBJ));
169	renderer->oam = &video->oam;
170	video->renderer->init(video->renderer);
171}
172
173void DSVideoDeinit(struct DSVideo* video) {
174	DSVideoAssociateRenderer(video, &dummyRenderer);
175	mappedMemoryFree(video->vram, DS_SIZE_VRAM);
176}
177
178void _startHdraw7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
179	struct DSVideo* video = context;
180	GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
181	dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
182	video->event7.callback = _startHblank7;
183	mTimingSchedule(timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
184
185	video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
186
187	if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
188		dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
189		if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
190			DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VCOUNTER);
191		}
192	} else {
193		dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
194	}
195	video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
196
197	switch (video->vcount) {
198	case DS_VIDEO_VERTICAL_PIXELS:
199		video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
200		if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
201			DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VBLANK);
202		}
203		break;
204	case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
205		video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
206		break;
207	}
208}
209
210void _startHblank7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
211	struct DSVideo* video = context;
212	GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
213	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
214	video->event7.callback = _startHdraw7;
215	mTimingSchedule(timing, &video->event7, DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
216
217	// Begin Hblank
218	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
219
220	if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
221		DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_HBLANK);
222	}
223	video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
224}
225
226void _startHdraw9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
227	struct DSVideo* video = context;
228	GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
229	dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
230	video->event9.callback = _startHblank9;
231	mTimingSchedule(timing, &video->event9, (DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH) * 2 - cyclesLate);
232
233	++video->vcount;
234	if (video->vcount == DS_VIDEO_VERTICAL_TOTAL_PIXELS) {
235		video->vcount = 0;
236	}
237	video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
238
239	if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
240		dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
241		if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
242			DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VCOUNTER);
243		}
244	} else {
245		dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
246	}
247	video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
248
249	// Note: state may be recorded during callbacks, so ensure it is consistent!
250	switch (video->vcount) {
251	case 0:
252		DSFrameStarted(video->p);
253		break;
254	case DS_VIDEO_VERTICAL_PIXELS:
255		video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
256		if (video->frameskipCounter <= 0) {
257			video->renderer->finishFrame(video->renderer);
258		}
259		if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
260			DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VBLANK);
261		}
262		DSFrameEnded(video->p);
263		--video->frameskipCounter;
264		if (video->frameskipCounter < 0) {
265			mCoreSyncPostFrame(video->p->sync);
266			video->frameskipCounter = video->frameskip;
267		}
268		++video->frameCounter;
269		break;
270	case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
271		video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
272		break;
273	}
274}
275
276void _startHblank9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
277	struct DSVideo* video = context;
278	GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
279	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
280	video->event9.callback = _startHdraw9;
281	mTimingSchedule(timing, &video->event9, (DS9_VIDEO_HBLANK_LENGTH * 2) - cyclesLate);
282
283	// Begin Hblank
284	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
285	if (video->vcount < DS_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
286		video->renderer->drawScanline(video->renderer, video->vcount);
287	}
288
289	if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
290		DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_HBLANK);
291	}
292	video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
293}
294
295void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value) {
296	dscore->memory.io[DS_REG_DISPSTAT >> 1] &= 0x7;
297	dscore->memory.io[DS_REG_DISPSTAT >> 1] |= value;
298	// TODO: Does a VCounter IRQ trigger on write?
299}
300
301void DSVideoConfigureVRAM(struct DS* ds, int index, uint8_t value) {
302	struct DSMemory* memory = &ds->memory;
303	struct DSVRAMBankInfo info = _vramInfo[index][value & 0x7];
304	memset(&memory->vramMirror[index], 0, sizeof(memory->vramMirror[index]));
305	memset(&memory->vramMode[index], 0, sizeof(memory->vramMode[index]));
306	if (!(value & 0x80) || !info.mirrorSize) {
307		return;
308	}
309	uint32_t size = _vramSize[index] >> DS_VRAM_OFFSET;
310	memory->vramMode[index][info.mode] = 0xFFFF;
311	uint32_t offset = info.base + info.offset[(value >> 3) & 3];
312	uint32_t i, j;
313	for (j = offset; j < 0x40; j += info.mirrorSize) {
314		for (i = 0; i < size; ++i) {
315			memory->vramMirror[index][i + j] = 1 << index;
316		}
317	}
318	switch (info.mode) {
319	case 0:
320		for (i = 0; i < size; ++i) {
321			ds->video.vramABG[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
322			ds->video.renderer->vramABG[offset + i] = ds->video.vramABG[offset + i];
323		}
324		break;
325	case 1:
326		for (i = 0; i < size; ++i) {
327			ds->video.vramBBG[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
328			ds->video.renderer->vramBBG[offset + i] = ds->video.vramBBG[offset + i];
329		}
330		break;
331	case 2:
332		for (i = 0; i < size; ++i) {
333			ds->video.vramAOBJ[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
334			ds->video.renderer->vramAOBJ[offset + i] = ds->video.vramAOBJ[offset + i];
335		}
336		break;
337	case 3:
338		for (i = 0; i < size; ++i) {
339			ds->video.vramBOBJ[offset + i] = &memory->vramBank[index][i << (DS_VRAM_OFFSET - 1)];
340			ds->video.renderer->vramBOBJ[offset + i] = ds->video.vramBOBJ[offset + i];
341		}
342		break;
343	}
344}
345
346static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer) {
347	UNUSED(renderer);
348	// Nothing to do
349}
350
351static void DSVideoDummyRendererReset(struct DSVideoRenderer* renderer) {
352	UNUSED(renderer);
353	// Nothing to do
354}
355
356static void DSVideoDummyRendererDeinit(struct DSVideoRenderer* renderer) {
357	UNUSED(renderer);
358	// Nothing to do
359}
360
361static uint16_t DSVideoDummyRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
362	UNUSED(renderer);
363	return value;
364}
365
366static void DSVideoDummyRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
367	UNUSED(value);
368	UNUSED(address);
369	UNUSED(value);
370}
371
372static void DSVideoDummyRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
373	UNUSED(renderer);
374	UNUSED(y);
375	// Nothing to do
376}
377
378static void DSVideoDummyRendererFinishFrame(struct DSVideoRenderer* renderer) {
379	UNUSED(renderer);
380	// Nothing to do
381}
382
383static void DSVideoDummyRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
384	UNUSED(renderer);
385	UNUSED(stride);
386	UNUSED(pixels);
387	// Nothing to do
388}
389
390static void DSVideoDummyRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
391	UNUSED(renderer);
392	UNUSED(stride);
393	UNUSED(pixels);
394	// Nothing to do
395}