all repos — mgba @ bb83e78af97e3da21522a75c7349d6b52e86804a

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/gba/video.h>
 11
 12#include <mgba-util/memory.h>
 13
 14mLOG_DEFINE_CATEGORY(DS_VIDEO, "DS Video");
 15
 16static void _startHblank7(struct mTiming*, void* context, uint32_t cyclesLate);
 17static void _startHdraw7(struct mTiming*, void* context, uint32_t cyclesLate);
 18static void _startHblank9(struct mTiming*, void* context, uint32_t cyclesLate);
 19static void _startHdraw9(struct mTiming*, void* context, uint32_t cyclesLate);
 20
 21void DSVideoInit(struct DSVideo* video) {
 22	video->vram = NULL;
 23	video->frameskip = 0;
 24	video->event7.name = "DS7 Video";
 25	video->event7.callback = NULL;
 26	video->event7.context = video;
 27	video->event7.priority = 8;
 28	video->event9.name = "DS9 Video";
 29	video->event9.callback = NULL;
 30	video->event9.context = video;
 31	video->event9.priority = 8;
 32}
 33
 34void DSVideoReset(struct DSVideo* video) {
 35	video->vcount = 0;
 36	video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
 37	video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
 38
 39	video->event7.callback = _startHblank7;
 40	video->event9.callback = _startHblank9;
 41	mTimingSchedule(&video->p->ds7.timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH);
 42	mTimingSchedule(&video->p->ds9.timing, &video->event9, DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH);
 43
 44	video->frameCounter = 0;
 45	video->frameskipCounter = 0;
 46
 47	if (video->vram) {
 48		mappedMemoryFree(video->vram, DS_SIZE_VRAM);
 49	}
 50	video->vram = anonymousMemoryMap(DS_SIZE_VRAM);
 51}
 52
 53void DSVideoDeinit(struct DSVideo* video) {
 54	mappedMemoryFree(video->vram, DS_SIZE_VRAM);
 55}
 56
 57void _startHdraw7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 58	struct DSVideo* video = context;
 59	GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
 60	dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
 61	video->event7.callback = _startHblank7;
 62	mTimingSchedule(timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
 63
 64	++video->vcount;
 65	if (video->vcount == DS_VIDEO_VERTICAL_TOTAL_PIXELS) {
 66		video->vcount = 0;
 67	}
 68	video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
 69
 70	if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
 71		dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
 72		if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
 73			DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VCOUNTER);
 74		}
 75	} else {
 76		dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
 77	}
 78	video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
 79
 80	switch (video->vcount) {
 81	case DS_VIDEO_VERTICAL_PIXELS:
 82		video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
 83		if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
 84			DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VBLANK);
 85		}
 86		break;
 87	case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
 88		video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
 89		break;
 90	}
 91}
 92
 93void _startHblank7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
 94	struct DSVideo* video = context;
 95	GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
 96	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
 97	video->event7.callback = _startHdraw7;
 98	mTimingSchedule(timing, &video->event7, DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
 99
100	// Begin Hblank
101	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
102
103	if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
104		DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_HBLANK);
105	}
106	video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
107}
108
109void _startHdraw9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
110	struct DSVideo* video = context;
111	GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
112	dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
113	video->event9.callback = _startHblank9;
114	mTimingSchedule(timing, &video->event9, DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH - cyclesLate);
115
116	++video->vcount;
117	if (video->vcount == DS_VIDEO_VERTICAL_TOTAL_PIXELS) {
118		video->vcount = 0;
119	}
120	video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
121
122	if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
123		dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
124		if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
125			DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VCOUNTER);
126		}
127	} else {
128		dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
129	}
130	video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
131
132	// Note: state may be recorded during callbacks, so ensure it is consistent!
133	switch (video->vcount) {
134	case 0:
135		DSFrameStarted(video->p);
136		break;
137	case DS_VIDEO_VERTICAL_PIXELS:
138		video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
139		if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
140			DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VBLANK);
141		}
142		DSFrameEnded(video->p);
143		--video->frameskipCounter;
144		if (video->frameskipCounter < 0) {
145			mCoreSyncPostFrame(video->p->sync);
146			video->frameskipCounter = video->frameskip;
147		}
148		++video->frameCounter;
149		break;
150	case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
151		video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
152		break;
153	}
154}
155
156void _startHblank9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
157	struct DSVideo* video = context;
158	GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
159	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
160	video->event9.callback = _startHdraw9;
161	mTimingSchedule(timing, &video->event9, DS9_VIDEO_HBLANK_LENGTH - cyclesLate);
162
163	// Begin Hblank
164	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
165
166	if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
167		DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_HBLANK);
168	}
169	video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
170}
171
172void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value) {
173	dscore->memory.io[DS_REG_DISPSTAT >> 1] &= 0x7;
174	dscore->memory.io[DS_REG_DISPSTAT >> 1] |= value;
175	// TODO: Does a VCounter IRQ trigger on write?
176}