all repos — mgba @ 5ad70925670fcc5069eb093c264d62f45c6b7131

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) * 2);
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->palette = video->palette;
162	renderer->vram = video->vram;
163	renderer->oam = &video->oam;
164	video->renderer->init(video->renderer);
165}
166
167void DSVideoDeinit(struct DSVideo* video) {
168	DSVideoAssociateRenderer(video, &dummyRenderer);
169	mappedMemoryFree(video->vram, DS_SIZE_VRAM);
170}
171
172void _startHdraw7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
173	struct DSVideo* video = context;
174	GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
175	dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
176	video->event7.callback = _startHblank7;
177	mTimingSchedule(timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
178
179	video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
180
181	if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
182		dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
183		if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
184			DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VCOUNTER);
185		}
186	} else {
187		dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
188	}
189	video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
190
191	switch (video->vcount) {
192	case DS_VIDEO_VERTICAL_PIXELS:
193		video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
194		if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
195			DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VBLANK);
196		}
197		break;
198	case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
199		video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
200		break;
201	}
202}
203
204void _startHblank7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
205	struct DSVideo* video = context;
206	GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
207	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
208	video->event7.callback = _startHdraw7;
209	mTimingSchedule(timing, &video->event7, DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
210
211	// Begin Hblank
212	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
213
214	if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
215		DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_HBLANK);
216	}
217	video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
218}
219
220void _startHdraw9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
221	struct DSVideo* video = context;
222	GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
223	dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
224	video->event9.callback = _startHblank9;
225	mTimingSchedule(timing, &video->event9, (DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH) * 2 - cyclesLate);
226
227	++video->vcount;
228	if (video->vcount == DS_VIDEO_VERTICAL_TOTAL_PIXELS) {
229		video->vcount = 0;
230	}
231	video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
232
233	if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
234		dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
235		if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
236			DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VCOUNTER);
237		}
238	} else {
239		dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
240	}
241	video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
242
243	// Note: state may be recorded during callbacks, so ensure it is consistent!
244	switch (video->vcount) {
245	case 0:
246		DSFrameStarted(video->p);
247		break;
248	case DS_VIDEO_VERTICAL_PIXELS:
249		video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
250		if (video->frameskipCounter <= 0) {
251			video->renderer->finishFrame(video->renderer);
252		}
253		if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
254			DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VBLANK);
255		}
256		DSFrameEnded(video->p);
257		--video->frameskipCounter;
258		if (video->frameskipCounter < 0) {
259			mCoreSyncPostFrame(video->p->sync);
260			video->frameskipCounter = video->frameskip;
261		}
262		++video->frameCounter;
263		break;
264	case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
265		video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
266		break;
267	}
268}
269
270void _startHblank9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
271	struct DSVideo* video = context;
272	GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
273	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
274	video->event9.callback = _startHdraw9;
275	mTimingSchedule(timing, &video->event9, (DS9_VIDEO_HBLANK_LENGTH * 2) - cyclesLate);
276
277	// Begin Hblank
278	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
279	if (video->vcount < DS_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
280		video->renderer->drawScanline(video->renderer, video->vcount);
281	}
282
283	if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
284		DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_HBLANK);
285	}
286	video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
287}
288
289void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value) {
290	dscore->memory.io[DS_REG_DISPSTAT >> 1] &= 0x7;
291	dscore->memory.io[DS_REG_DISPSTAT >> 1] |= value;
292	// TODO: Does a VCounter IRQ trigger on write?
293}
294
295void DSVideoConfigureVRAM(struct DSMemory* memory, int index, uint8_t value) {
296	struct DSVRAMBankInfo info = _vramInfo[index][value & 0x7];
297	memset(&memory->vramMirror[index], 0, sizeof(memory->vramMirror[index]));
298	memset(&memory->vramMode[index], 0, sizeof(memory->vramMode[index]));
299	if (!(value & 0x80) || !info.mirrorSize) {
300		return;
301	}
302	uint32_t size = _vramSize[index] >> DS_VRAM_OFFSET;
303	memory->vramMode[index][info.mode] = 0xFFFF;
304	uint32_t offset = info.base + info.offset[(value >> 3) & 3];
305	uint32_t i, j;
306	for (j = offset; j < 0x40; j += info.mirrorSize) {
307		for (i = 0; i < size; ++i) {
308			memory->vramMirror[index][i + j] = 1 << index;
309		}
310	}
311}
312
313static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer) {
314	UNUSED(renderer);
315	// Nothing to do
316}
317
318static void DSVideoDummyRendererReset(struct DSVideoRenderer* renderer) {
319	UNUSED(renderer);
320	// Nothing to do
321}
322
323static void DSVideoDummyRendererDeinit(struct DSVideoRenderer* renderer) {
324	UNUSED(renderer);
325	// Nothing to do
326}
327
328static uint16_t DSVideoDummyRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
329	UNUSED(renderer);
330	return value;
331}
332
333static void DSVideoDummyRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
334	UNUSED(renderer);
335	UNUSED(y);
336	// Nothing to do
337}
338
339static void DSVideoDummyRendererFinishFrame(struct DSVideoRenderer* renderer) {
340	UNUSED(renderer);
341	// Nothing to do
342}
343
344static void DSVideoDummyRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
345	UNUSED(renderer);
346	UNUSED(stride);
347	UNUSED(pixels);
348	// Nothing to do
349}
350
351static void DSVideoDummyRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
352	UNUSED(renderer);
353	UNUSED(stride);
354	UNUSED(pixels);
355	// Nothing to do
356}