all repos — mgba @ 7bc39569edec5e719126e8c3a977a3e5ec3d6c3c

mGBA Game Boy Advance Emulator

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

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