all repos — mgba @ f78f45b60fb1d6ad4ea6d22b55640aefad5c5c0e

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