all repos — mgba @ c5e5efca2e1876dff5390af4168df5d36a7afb9f

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->dispstat = 0;
 40	video->vcount = 0;
 41
 42	video->lastHblank = 0;
 43	video->nextHblank = VIDEO_HDRAW_LENGTH;
 44	video->nextEvent = video->nextHblank;
 45	video->eventDiff = video->nextEvent;
 46
 47	video->nextHblankIRQ = 0;
 48	video->nextVblankIRQ = 0;
 49	video->nextVcounterIRQ = 0;
 50
 51	if (video->vram) {
 52		mappedMemoryFree(video->vram, SIZE_VRAM);
 53	}
 54	video->vram = anonymousMemoryMap(SIZE_VRAM);
 55	video->renderer->vram = video->vram;
 56
 57	int i;
 58	for (i = 0; i < 128; ++i) {
 59		video->oam.raw[i * 4] = 0x0200;
 60		video->oam.raw[i * 4 + 1] = 0x0000;
 61		video->oam.raw[i * 4 + 2] = 0x0000;
 62		video->oam.raw[i * 4 + 3] = 0x0000;
 63	}
 64
 65	video->renderer->deinit(video->renderer);
 66	video->renderer->init(video->renderer);
 67}
 68
 69void GBAVideoDeinit(struct GBAVideo* video) {
 70	GBAVideoAssociateRenderer(video, &dummyRenderer);
 71	mappedMemoryFree(video->vram, SIZE_VRAM);
 72}
 73
 74void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer* renderer) {
 75	video->renderer->deinit(video->renderer);
 76	video->renderer = renderer;
 77	renderer->palette = video->palette;
 78	renderer->vram = video->vram;
 79	renderer->oam = &video->oam;
 80	video->renderer->init(video->renderer);
 81}
 82
 83int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
 84	video->nextEvent -= cycles;
 85	video->eventDiff += cycles;
 86	if (video->nextEvent <= 0) {
 87		int32_t lastEvent = video->nextEvent;
 88		video->lastHblank -= video->eventDiff;
 89		video->nextHblank -= video->eventDiff;
 90		video->nextHblankIRQ -= video->eventDiff;
 91		video->nextVcounterIRQ -= video->eventDiff;
 92
 93		if (GBARegisterDISPSTATIsInHblank(video->dispstat)) {
 94			// End Hblank
 95			video->dispstat = GBARegisterDISPSTATClearInHblank(video->dispstat);
 96			video->nextEvent = video->nextHblank;
 97
 98			++video->vcount;
 99			video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
100
101			switch (video->vcount) {
102			case VIDEO_VERTICAL_PIXELS:
103				video->dispstat = GBARegisterDISPSTATFillInVblank(video->dispstat);
104				if (GBASyncDrawingFrame(video->p->sync)) {
105					video->renderer->finishFrame(video->renderer);
106				}
107				video->nextVblankIRQ = video->nextEvent + VIDEO_TOTAL_LENGTH;
108				GBAMemoryRunVblankDMAs(video->p, lastEvent);
109				if (GBARegisterDISPSTATIsVblankIRQ(video->dispstat)) {
110					GBARaiseIRQ(video->p, IRQ_VBLANK);
111				}
112				GBASyncPostFrame(video->p->sync);
113				break;
114			case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
115				if (video->p->rr) {
116					GBARRNextFrame(video->p->rr);
117				}
118				video->dispstat = GBARegisterDISPSTATClearInVblank(video->dispstat);
119				break;
120			case VIDEO_VERTICAL_TOTAL_PIXELS:
121				video->vcount = 0;
122				video->p->memory.io[REG_VCOUNT >> 1] = 0;
123				break;
124			}
125
126			if (video->vcount == GBARegisterDISPSTATGetVcountSetting(video->dispstat)) {
127				video->dispstat = GBARegisterDISPSTATFillVcounter(video->dispstat);
128				if (GBARegisterDISPSTATIsVcounterIRQ(video->dispstat)) {
129					GBARaiseIRQ(video->p, IRQ_VCOUNTER);
130					video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
131				}
132			} else {
133				video->dispstat = GBARegisterDISPSTATClearVcounter(video->dispstat);
134			}
135		} else {
136			// Begin Hblank
137			video->dispstat = GBARegisterDISPSTATFillInHblank(video->dispstat);
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 (GBARegisterDISPSTATIsHblankIRQ(video->dispstat)) {
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->dispstat & 0x7;
159	return video->nextEvent;
160}
161
162void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value) {
163	video->dispstat &= 0x7;
164	video->dispstat |= value & 0xFFF8;
165
166	if (GBARegisterDISPSTATIsVcounterIRQ(video->dispstat)) {
167		// FIXME: this can be too late if we're in the middle of an Hblank
168		video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (GBARegisterDISPSTATGetVcountSetting(video->dispstat) - video->vcount) * VIDEO_HORIZONTAL_LENGTH;
169		if (video->nextVcounterIRQ < video->nextEvent) {
170			video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
171		}
172	}
173}
174
175static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer) {
176	UNUSED(renderer);
177	// Nothing to do
178}
179
180static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer) {
181	UNUSED(renderer);
182	// Nothing to do
183}
184
185static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer) {
186	UNUSED(renderer);
187	// Nothing to do
188}
189
190static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
191	UNUSED(renderer);
192	UNUSED(address);
193	return value;
194}
195
196static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
197	UNUSED(renderer);
198	UNUSED(address);
199	UNUSED(value);
200	// Nothing to do
201}
202
203static void GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
204	UNUSED(renderer);
205	UNUSED(oam);
206	// Nothing to do
207}
208
209static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
210	UNUSED(renderer);
211	UNUSED(y);
212	// Nothing to do
213}
214
215static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
216	UNUSED(renderer);
217	// Nothing to do
218}
219
220static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
221	UNUSED(renderer);
222	UNUSED(stride);
223	UNUSED(pixels);
224	// Nothing to do
225}
226
227
228void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state) {
229	memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
230	memcpy(state->oam, video->oam.raw, SIZE_OAM);
231	memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
232	state->io[REG_DISPSTAT >> 1] = video->dispstat;
233	state->video.nextEvent = video->nextEvent;
234	state->video.eventDiff = video->eventDiff;
235	state->video.lastHblank = video->lastHblank;
236	state->video.nextHblank = video->nextHblank;
237	state->video.nextHblankIRQ = video->nextHblankIRQ;
238	state->video.nextVblankIRQ = video->nextVblankIRQ;
239	state->video.nextVcounterIRQ = video->nextVcounterIRQ;
240}
241
242void GBAVideoDeserialize(struct GBAVideo* video, struct GBASerializedState* state) {
243	memcpy(video->renderer->vram, state->vram, SIZE_VRAM);
244	int i;
245	for (i = 0; i < SIZE_OAM; i += 2) {
246		GBAStore16(video->p->cpu, BASE_OAM | i, state->oam[i >> 1], 0);
247	}
248	for (i = 0; i < SIZE_PALETTE_RAM; i += 2) {
249		GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, state->pram[i >> 1], 0);
250	}
251	video->dispstat = state->io[REG_DISPSTAT >> 1];
252	video->nextEvent = state->video.nextEvent;
253	video->eventDiff = state->video.eventDiff;
254	video->lastHblank = state->video.lastHblank;
255	video->nextHblank = state->video.nextHblank;
256	video->nextHblankIRQ = state->video.nextHblankIRQ;
257	video->nextVblankIRQ = state->video.nextVblankIRQ;
258	video->nextVcounterIRQ = state->video.nextVcounterIRQ;
259	video->vcount = state->io[REG_VCOUNT >> 1];
260}