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/gba/video.h>
7 
8 #include <mgba/core/sync.h>
9 #include <mgba/core/cache-set.h>
10 #include <mgba/internal/arm/macros.h>
11 #include <mgba/internal/gba/dma.h>
12 #include <mgba/internal/gba/gba.h>
13 #include <mgba/internal/gba/io.h>
14 #include <mgba/internal/gba/renderers/cache-set.h>
15 #include <mgba/internal/gba/serialize.h>
16 
17 #include <mgba-util/memory.h>
18 
19 mLOG_DEFINE_CATEGORY(GBA_VIDEO, "GBA Video", "gba.video");
20 
21 static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer);
22 static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer);
23 static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer);
24 static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
25 static void GBAVideoDummyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
26 static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
27 static void GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
28 static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
29 static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer);
30 static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
31 static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
32 
33 static void _startHblank(struct mTiming*, void* context, uint32_t cyclesLate);
34 static void _midHblank(struct mTiming*, void* context, uint32_t cyclesLate);
35 static void _startHdraw(struct mTiming*, void* context, uint32_t cyclesLate);
36 
37 MGBA_EXPORT const int GBAVideoObjSizes[16][2] = {
38 	{ 8, 8 },
39 	{ 16, 16 },
40 	{ 32, 32 },
41 	{ 64, 64 },
42 	{ 16, 8 },
43 	{ 32, 8 },
44 	{ 32, 16 },
45 	{ 64, 32 },
46 	{ 8, 16 },
47 	{ 8, 32 },
48 	{ 16, 32 },
49 	{ 32, 64 },
50 	{ 0, 0 },
51 	{ 0, 0 },
52 	{ 0, 0 },
53 	{ 0, 0 },
54 };
55 
GBAVideoInit(struct GBAVideo * video)56 void GBAVideoInit(struct GBAVideo* video) {
57 	video->renderer = NULL;
58 	video->vram = anonymousMemoryMap(SIZE_VRAM);
59 	video->frameskip = 0;
60 	video->event.name = "GBA Video";
61 	video->event.callback = NULL;
62 	video->event.context = video;
63 	video->event.priority = 8;
64 }
65 
GBAVideoReset(struct GBAVideo * video)66 void GBAVideoReset(struct GBAVideo* video) {
67 	int32_t nextEvent = VIDEO_HDRAW_LENGTH;
68 	if (video->p->memory.fullBios) {
69 		video->vcount = 0;
70 	} else {
71 		// TODO: Verify exact scanline on hardware
72 		video->vcount = 0x7E;
73 		nextEvent = 170;
74 	}
75 	video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
76 
77 	video->event.callback = _startHblank;
78 	mTimingSchedule(&video->p->timing, &video->event, nextEvent);
79 
80 	video->frameCounter = 0;
81 	video->frameskipCounter = 0;
82 	video->shouldStall = 0;
83 
84 	memset(video->palette, 0, sizeof(video->palette));
85 	memset(video->oam.raw, 0, sizeof(video->oam.raw));
86 
87 	if (!video->renderer) {
88 		mLOG(GBA_VIDEO, FATAL, "No renderer associated");
89 		return;
90 	}
91 	video->renderer->vram = video->vram;
92 	video->renderer->reset(video->renderer);
93 }
94 
GBAVideoDeinit(struct GBAVideo * video)95 void GBAVideoDeinit(struct GBAVideo* video) {
96 	video->renderer->deinit(video->renderer);
97 	mappedMemoryFree(video->vram, SIZE_VRAM);
98 }
99 
GBAVideoDummyRendererCreate(struct GBAVideoRenderer * renderer)100 void GBAVideoDummyRendererCreate(struct GBAVideoRenderer* renderer) {
101 	static const struct GBAVideoRenderer dummyRenderer = {
102 		.init = GBAVideoDummyRendererInit,
103 		.reset = GBAVideoDummyRendererReset,
104 		.deinit = GBAVideoDummyRendererDeinit,
105 		.writeVideoRegister = GBAVideoDummyRendererWriteVideoRegister,
106 		.writeVRAM = GBAVideoDummyRendererWriteVRAM,
107 		.writePalette = GBAVideoDummyRendererWritePalette,
108 		.writeOAM = GBAVideoDummyRendererWriteOAM,
109 		.drawScanline = GBAVideoDummyRendererDrawScanline,
110 		.finishFrame = GBAVideoDummyRendererFinishFrame,
111 		.getPixels = GBAVideoDummyRendererGetPixels,
112 		.putPixels = GBAVideoDummyRendererPutPixels,
113 	};
114 	memcpy(renderer, &dummyRenderer, sizeof(*renderer));
115 }
116 
GBAVideoAssociateRenderer(struct GBAVideo * video,struct GBAVideoRenderer * renderer)117 void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer* renderer) {
118 	if (video->renderer) {
119 		video->renderer->deinit(video->renderer);
120 		renderer->cache = video->renderer->cache;
121 	} else {
122 		renderer->cache = NULL;
123 	}
124 	video->renderer = renderer;
125 	renderer->palette = video->palette;
126 	renderer->vram = video->vram;
127 	renderer->oam = &video->oam;
128 	video->renderer->init(video->renderer);
129 	video->renderer->reset(video->renderer);
130 	renderer->writeVideoRegister(renderer, REG_DISPCNT, video->p->memory.io[REG_DISPCNT >> 1]);
131 	renderer->writeVideoRegister(renderer, REG_GREENSWP, video->p->memory.io[REG_GREENSWP >> 1]);
132 	int address;
133 	for (address = REG_BG0CNT; address < 0x56; address += 2) {
134 		if (address == 0x4E) {
135 			continue;
136 		}
137 		renderer->writeVideoRegister(renderer, address, video->p->memory.io[address >> 1]);
138 	}
139 }
140 
_midHblank(struct mTiming * timing,void * context,uint32_t cyclesLate)141 void _midHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
142 	struct GBAVideo* video = context;
143 	GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
144 	dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
145 	video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
146 	video->event.callback = _startHdraw;
147 	mTimingSchedule(timing, &video->event, VIDEO_HBLANK_FLIP - cyclesLate);
148 }
149 
_startHdraw(struct mTiming * timing,void * context,uint32_t cyclesLate)150 void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
151 	struct GBAVideo* video = context;
152 	video->event.callback = _startHblank;
153 	mTimingSchedule(timing, &video->event, VIDEO_HDRAW_LENGTH - cyclesLate);
154 
155 	++video->vcount;
156 	if (video->vcount == VIDEO_VERTICAL_TOTAL_PIXELS) {
157 		video->vcount = 0;
158 	}
159 	video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
160 
161 	if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
162 		video->shouldStall = 1;
163 	}
164 
165 	GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
166 	if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
167 		dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
168 		if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
169 			GBARaiseIRQ(video->p, IRQ_VCOUNTER, cyclesLate);
170 		}
171 	} else {
172 		dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
173 	}
174 	video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
175 
176 	// Note: state may be recorded during callbacks, so ensure it is consistent!
177 	switch (video->vcount) {
178 	case 0:
179 		GBAFrameStarted(video->p);
180 		break;
181 	case GBA_VIDEO_VERTICAL_PIXELS:
182 		video->p->memory.io[REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
183 		if (video->frameskipCounter <= 0) {
184 			video->renderer->finishFrame(video->renderer);
185 		}
186 		GBADMARunVblank(video->p, -cyclesLate);
187 		if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
188 			GBARaiseIRQ(video->p, IRQ_VBLANK, cyclesLate);
189 		}
190 		GBAFrameEnded(video->p);
191 		mCoreSyncPostFrame(video->p->sync);
192 		--video->frameskipCounter;
193 		if (video->frameskipCounter < 0) {
194 			video->frameskipCounter = video->frameskip;
195 		}
196 		++video->frameCounter;
197 		video->p->earlyExit = true;
198 		break;
199 	case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
200 		video->p->memory.io[REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
201 		break;
202 	}
203 }
204 
_startHblank(struct mTiming * timing,void * context,uint32_t cyclesLate)205 void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
206 	struct GBAVideo* video = context;
207 	video->event.callback = _midHblank;
208 	mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - VIDEO_HBLANK_FLIP - cyclesLate);
209 
210 	// Begin Hblank
211 	GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
212 	dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
213 	if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
214 		video->renderer->drawScanline(video->renderer, video->vcount);
215 	}
216 
217 	if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
218 		GBADMARunHblank(video->p, -cyclesLate);
219 	}
220 	if (video->vcount >= 2 && video->vcount < GBA_VIDEO_VERTICAL_PIXELS + 2) {
221 		GBADMARunDisplayStart(video->p, -cyclesLate);
222 	}
223 	if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
224 		GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate);
225 	}
226 	video->shouldStall = 0;
227 	video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
228 }
229 
GBAVideoWriteDISPSTAT(struct GBAVideo * video,uint16_t value)230 void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value) {
231 	video->p->memory.io[REG_DISPSTAT >> 1] &= 0x7;
232 	video->p->memory.io[REG_DISPSTAT >> 1] |= value;
233 	// TODO: Does a VCounter IRQ trigger on write?
234 }
235 
GBAVideoDummyRendererInit(struct GBAVideoRenderer * renderer)236 static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer) {
237 	UNUSED(renderer);
238 	// Nothing to do
239 }
240 
GBAVideoDummyRendererReset(struct GBAVideoRenderer * renderer)241 static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer) {
242 	UNUSED(renderer);
243 	// Nothing to do
244 }
245 
GBAVideoDummyRendererDeinit(struct GBAVideoRenderer * renderer)246 static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer) {
247 	UNUSED(renderer);
248 	// Nothing to do
249 }
250 
GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer * renderer,uint32_t address,uint16_t value)251 static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
252 	if (renderer->cache) {
253 		GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
254 	}
255 	switch (address) {
256 	case REG_DISPCNT:
257 		value &= 0xFFF7;
258 		break;
259 	case REG_BG0CNT:
260 	case REG_BG1CNT:
261 		value &= 0xDFFF;
262 		break;
263 	case REG_BG2CNT:
264 	case REG_BG3CNT:
265 		value &= 0xFFFF;
266 		break;
267 	case REG_BG0HOFS:
268 	case REG_BG0VOFS:
269 	case REG_BG1HOFS:
270 	case REG_BG1VOFS:
271 	case REG_BG2HOFS:
272 	case REG_BG2VOFS:
273 	case REG_BG3HOFS:
274 	case REG_BG3VOFS:
275 		value &= 0x01FF;
276 		break;
277 	case REG_BLDCNT:
278 		value &= 0x3FFF;
279 		break;
280 	case REG_BLDALPHA:
281 		value &= 0x1F1F;
282 		break;
283 	case REG_WININ:
284 	case REG_WINOUT:
285 		value &= 0x3F3F;
286 		break;
287 	default:
288 		break;
289 	}
290 	return value;
291 }
292 
GBAVideoDummyRendererWriteVRAM(struct GBAVideoRenderer * renderer,uint32_t address)293 static void GBAVideoDummyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
294 	if (renderer->cache) {
295 		mCacheSetWriteVRAM(renderer->cache, address);
296 	}
297 }
298 
GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer * renderer,uint32_t address,uint16_t value)299 static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
300 	if (renderer->cache) {
301 		mCacheSetWritePalette(renderer->cache, address >> 1, mColorFrom555(value));
302 	}
303 }
304 
GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer * renderer,uint32_t oam)305 static void GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
306 	UNUSED(renderer);
307 	UNUSED(oam);
308 	// Nothing to do
309 }
310 
GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer * renderer,int y)311 static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
312 	UNUSED(renderer);
313 	UNUSED(y);
314 	// Nothing to do
315 }
316 
GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer * renderer)317 static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
318 	UNUSED(renderer);
319 	// Nothing to do
320 }
321 
GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer * renderer,size_t * stride,const void ** pixels)322 static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
323 	UNUSED(renderer);
324 	UNUSED(stride);
325 	UNUSED(pixels);
326 	// Nothing to do
327 }
328 
GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer * renderer,size_t stride,const void * pixels)329 static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
330 	UNUSED(renderer);
331 	UNUSED(stride);
332 	UNUSED(pixels);
333 	// Nothing to do
334 }
335 
GBAVideoSerialize(const struct GBAVideo * video,struct GBASerializedState * state)336 void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) {
337 	memcpy(state->vram, video->vram, SIZE_VRAM);
338 	memcpy(state->oam, video->oam.raw, SIZE_OAM);
339 	memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
340 	STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent);
341 	int32_t flags = 0;
342 	if (video->event.callback == _startHdraw) {
343 		flags = GBASerializedVideoFlagsSetMode(flags, 1);
344 	} else if (video->event.callback == _startHblank) {
345 		flags = GBASerializedVideoFlagsSetMode(flags, 2);
346 	} else if (video->event.callback == _midHblank) {
347 		flags = GBASerializedVideoFlagsSetMode(flags, 3);
348 	}
349 	STORE_32(flags, 0, &state->video.flags);
350 	STORE_32(video->frameCounter, 0, &state->video.frameCounter);
351 }
352 
GBAVideoDeserialize(struct GBAVideo * video,const struct GBASerializedState * state)353 void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) {
354 	memcpy(video->vram, state->vram, SIZE_VRAM);
355 	uint16_t value;
356 	int i;
357 	for (i = 0; i < SIZE_OAM; i += 2) {
358 		LOAD_16(value, i, state->oam);
359 		GBAStore16(video->p->cpu, BASE_OAM | i, value, 0);
360 	}
361 	for (i = 0; i < SIZE_PALETTE_RAM; i += 2) {
362 		LOAD_16(value, i, state->pram);
363 		GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0);
364 	}
365 	LOAD_32(video->frameCounter, 0, &state->video.frameCounter);
366 
367 	video->shouldStall = 0;
368 	int32_t flags;
369 	LOAD_32(flags, 0, &state->video.flags);
370 	GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1];
371 	switch (GBASerializedVideoFlagsGetMode(flags)) {
372 	case 0:
373 		if (GBARegisterDISPSTATIsInHblank(dispstat)) {
374 			video->event.callback = _startHdraw;
375 		} else {
376 			video->event.callback = _startHblank;
377 		}
378 		break;
379 	case 1:
380 		video->event.callback = _startHdraw;
381 		break;
382 	case 2:
383 		video->event.callback = _startHblank;
384 		video->shouldStall = 1;
385 		break;
386 	case 3:
387 		video->event.callback = _midHblank;
388 		break;
389 	}
390 	uint32_t when;
391 	LOAD_32(when, 0, &state->video.nextEvent);
392 	mTimingSchedule(&video->p->timing, &video->event, when);
393 
394 	LOAD_16(video->vcount, REG_VCOUNT, state->io);
395 	video->renderer->reset(video->renderer);
396 }
397