all repos — mgba @ 2b3631dc91ec424429d4dc3259c8b8f81575bf09

mGBA Game Boy Advance Emulator

src/gba/gba-video.c (view raw)

  1#include "gba-video.h"
  2
  3#include "gba.h"
  4#include "gba-io.h"
  5#include "gba-rr.h"
  6#include "gba-serialize.h"
  7#include "gba-thread.h"
  8
  9#include "util/memory.h"
 10
 11static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer);
 12static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer);
 13static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer);
 14static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 15static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 16static void GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
 17static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
 18static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer);
 19static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels);
 20
 21static struct GBAVideoRenderer dummyRenderer = {
 22	.init = GBAVideoDummyRendererInit,
 23	.reset = GBAVideoDummyRendererReset,
 24	.deinit = GBAVideoDummyRendererDeinit,
 25	.writeVideoRegister = GBAVideoDummyRendererWriteVideoRegister,
 26	.writePalette = GBAVideoDummyRendererWritePalette,
 27	.writeOAM = GBAVideoDummyRendererWriteOAM,
 28	.drawScanline = GBAVideoDummyRendererDrawScanline,
 29	.finishFrame = GBAVideoDummyRendererFinishFrame,
 30	.getPixels = GBAVideoDummyRendererGetPixels
 31};
 32
 33void GBAVideoInit(struct GBAVideo* video) {
 34	video->renderer = &dummyRenderer;
 35	video->vram = 0;
 36}
 37
 38void GBAVideoReset(struct GBAVideo* video) {
 39	video->inHblank = 0;
 40	video->inVblank = 0;
 41	video->vcounter = 0;
 42	video->vblankIRQ = 0;
 43	video->hblankIRQ = 0;
 44	video->vcounterIRQ = 0;
 45	video->vcountSetting = 0;
 46
 47	video->vcount = 0;
 48
 49	video->lastHblank = 0;
 50	video->nextHblank = VIDEO_HDRAW_LENGTH;
 51	video->nextEvent = video->nextHblank;
 52	video->eventDiff = video->nextEvent;
 53
 54	video->nextHblankIRQ = 0;
 55	video->nextVblankIRQ = 0;
 56	video->nextVcounterIRQ = 0;
 57
 58	if (video->vram) {
 59		mappedMemoryFree(video->vram, SIZE_VRAM);
 60	}
 61	video->vram = anonymousMemoryMap(SIZE_VRAM);
 62	video->renderer->vram = video->vram;
 63
 64	int i;
 65	for (i = 0; i < 128; ++i) {
 66		video->oam.raw[i * 4] = 0x0200;
 67		video->oam.raw[i * 4 + 1] = 0x0000;
 68		video->oam.raw[i * 4 + 2] = 0x0000;
 69		video->oam.raw[i * 4 + 3] = 0x0000;
 70	}
 71}
 72
 73void GBAVideoDeinit(struct GBAVideo* video) {
 74	GBAVideoAssociateRenderer(video, &dummyRenderer);
 75	mappedMemoryFree(video->vram, SIZE_VRAM);
 76}
 77
 78void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer* renderer) {
 79	video->renderer->deinit(video->renderer);
 80	video->renderer = renderer;
 81	renderer->palette = video->palette;
 82	renderer->vram = video->vram;
 83	renderer->oam = &video->oam;
 84	video->renderer->init(video->renderer);
 85}
 86
 87int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
 88	video->nextEvent -= cycles;
 89	video->eventDiff += cycles;
 90	if (video->nextEvent <= 0) {
 91		int32_t lastEvent = video->nextEvent;
 92		video->lastHblank -= video->eventDiff;
 93		video->nextHblank -= video->eventDiff;
 94		video->nextHblankIRQ -= video->eventDiff;
 95		video->nextVcounterIRQ -= video->eventDiff;
 96
 97		if (video->inHblank) {
 98			// End Hblank
 99			video->inHblank = 0;
100			video->nextEvent = video->nextHblank;
101
102			++video->vcount;
103			video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
104
105			switch (video->vcount) {
106			case VIDEO_VERTICAL_PIXELS:
107				video->inVblank = 1;
108				if (GBASyncDrawingFrame(video->p->sync)) {
109					video->renderer->finishFrame(video->renderer);
110				}
111				video->nextVblankIRQ = video->nextEvent + VIDEO_TOTAL_LENGTH;
112				GBAMemoryRunVblankDMAs(video->p, lastEvent);
113				if (video->vblankIRQ) {
114					GBARaiseIRQ(video->p, IRQ_VBLANK);
115				}
116				GBASyncPostFrame(video->p->sync);
117				break;
118			case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
119				if (video->p->rr) {
120					GBARRNextFrame(video->p->rr);
121				}
122				video->inVblank = 0;
123				break;
124			case VIDEO_VERTICAL_TOTAL_PIXELS:
125				video->vcount = 0;
126				video->p->memory.io[REG_VCOUNT >> 1] = 0;
127				break;
128			}
129
130			video->vcounter = video->vcount == video->vcountSetting;
131			if (video->vcounter && video->vcounterIRQ) {
132				GBARaiseIRQ(video->p, IRQ_VCOUNTER);
133				video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
134			}
135		} else {
136			// Begin Hblank
137			video->inHblank = 1;
138			video->lastHblank = video->nextHblank;
139			video->nextEvent = video->lastHblank + VIDEO_HBLANK_LENGTH;
140			video->nextHblank = video->nextEvent + VIDEO_HDRAW_LENGTH;
141			video->nextHblankIRQ = video->nextHblank;
142
143			if (video->vcount < VIDEO_VERTICAL_PIXELS && GBASyncDrawingFrame(video->p->sync)) {
144				video->renderer->drawScanline(video->renderer, video->vcount);
145			}
146
147			if (video->vcount < VIDEO_VERTICAL_PIXELS) {
148				GBAMemoryRunHblankDMAs(video->p, lastEvent);
149			}
150			if (video->hblankIRQ) {
151				GBARaiseIRQ(video->p, IRQ_HBLANK);
152			}
153		}
154
155		video->eventDiff = 0;
156	}
157	video->p->memory.io[REG_DISPSTAT >> 1] &= 0xFFF8;
158	video->p->memory.io[REG_DISPSTAT >> 1] |= (video->inVblank) | (video->inHblank << 1) | (video->vcounter << 2);
159	return video->nextEvent;
160}
161
162void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value) {
163	union GBARegisterDISPSTAT dispstat;
164	dispstat.packed = value;
165	video->vblankIRQ = dispstat.vblankIRQ;
166	video->hblankIRQ = dispstat.hblankIRQ;
167	video->vcounterIRQ = dispstat.vcounterIRQ;
168	video->vcountSetting = dispstat.vcountSetting;
169
170	if (video->vcounterIRQ) {
171		// FIXME: this can be too late if we're in the middle of an Hblank
172		video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (video->vcountSetting - video->vcount) * VIDEO_HORIZONTAL_LENGTH;
173		if (video->nextVcounterIRQ < video->nextEvent) {
174			video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
175		}
176	}
177}
178
179static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer) {
180	UNUSED(renderer);
181	// Nothing to do
182}
183
184static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer) {
185	UNUSED(renderer);
186	// Nothing to do
187}
188
189static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer) {
190	UNUSED(renderer);
191	// Nothing to do
192}
193
194static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
195	UNUSED(renderer);
196	UNUSED(address);
197	return value;
198}
199
200static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
201	UNUSED(renderer);
202	UNUSED(address);
203	UNUSED(value);
204	// Nothing to do
205}
206
207static void GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
208	UNUSED(renderer);
209	UNUSED(oam);
210	// Nothing to do
211}
212
213static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
214	UNUSED(renderer);
215	UNUSED(y);
216	// Nothing to do
217}
218
219static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
220	UNUSED(renderer);
221	// Nothing to do
222}
223
224static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
225	UNUSED(renderer);
226	UNUSED(stride);
227	UNUSED(pixels);
228	// Nothing to do
229}
230
231
232void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state) {
233	memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
234	memcpy(state->oam, video->oam.raw, SIZE_OAM);
235	memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
236	union GBARegisterDISPSTAT dispstat;
237	dispstat.inVblank = video->inVblank;
238	dispstat.inHblank = video->inHblank;
239	dispstat.vcounter = video->vcounter;
240	dispstat.vblankIRQ = video->vblankIRQ;
241	dispstat.hblankIRQ = video->hblankIRQ;
242	dispstat.vcounterIRQ = video->vcounterIRQ;
243	dispstat.vcountSetting = video->vcountSetting;
244	state->io[REG_DISPSTAT >> 1] = dispstat.packed;
245	state->video.nextEvent = video->nextEvent;
246	state->video.eventDiff = video->eventDiff;
247	state->video.lastHblank = video->lastHblank;
248	state->video.nextHblank = video->nextHblank;
249	state->video.nextHblankIRQ = video->nextHblankIRQ;
250	state->video.nextVblankIRQ = video->nextVblankIRQ;
251	state->video.nextVcounterIRQ = video->nextVcounterIRQ;
252}
253
254void GBAVideoDeserialize(struct GBAVideo* video, struct GBASerializedState* state) {
255	memcpy(video->renderer->vram, state->vram, SIZE_VRAM);
256	int i;
257	for (i = 0; i < SIZE_OAM; i += 2) {
258		GBAStore16(video->p->cpu, BASE_OAM | i, state->oam[i >> 1], 0);
259	}
260	for (i = 0; i < SIZE_PALETTE_RAM; i += 2) {
261		GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, state->pram[i >> 1], 0);
262	}
263	union GBARegisterDISPSTAT dispstat;
264	dispstat.packed = state->io[REG_DISPSTAT >> 1];
265	video->inVblank = dispstat.inVblank;
266	video->inHblank = dispstat.inHblank;
267	video->vcounter = dispstat.vcounter;
268	video->vblankIRQ = dispstat.vblankIRQ;
269	video->hblankIRQ = dispstat.hblankIRQ;
270	video->vcounterIRQ = dispstat.vcounterIRQ;
271	video->vcountSetting = dispstat.vcountSetting;
272	video->nextEvent = state->video.nextEvent;
273	video->eventDiff = state->video.eventDiff;
274	video->lastHblank = state->video.lastHblank;
275	video->nextHblank = state->video.nextHblank;
276	video->nextHblankIRQ = state->video.nextHblankIRQ;
277	video->nextVblankIRQ = state->video.nextVblankIRQ;
278	video->nextVcounterIRQ = state->video.nextVcounterIRQ;
279	video->vcount = state->io[REG_VCOUNT >> 1];
280}