all repos — mgba @ 9c30435c9fd12dcf5e79af281d44c82daf295ef2

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