all repos — mgba @ 6456a886695e6290fee373255fef014f47c20562

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