all repos — mgba @ 1589b26ade442d1779ac339ca1e1b56b17fede27

mGBA Game Boy Advance Emulator

src/gb/video.c (view raw)

  1/* Copyright (c) 2013-2016 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 "video.h"
  7
  8#include "gb/gb.h"
  9#include "gb/io.h"
 10
 11#include "util/memory.h"
 12
 13static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer);
 14static void GBVideoDummyRendererReset(struct GBVideoRenderer* renderer);
 15static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
 16static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
 17static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
 18static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint8_t oam);
 19static void GBVideoDummyRendererDrawScanline(struct GBVideoRenderer* renderer, int y);
 20static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer);
 21static void GBVideoDummyRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels);
 22
 23static struct GBVideoRenderer dummyRenderer = {
 24	.init = GBVideoDummyRendererInit,
 25	.reset = GBVideoDummyRendererReset,
 26	.deinit = GBVideoDummyRendererDeinit,
 27	.writeVideoRegister = GBVideoDummyRendererWriteVideoRegister,
 28	.writeVRAM = GBVideoDummyRendererWriteVRAM,
 29	.writeOAM = GBVideoDummyRendererWriteOAM,
 30	.drawScanline = GBVideoDummyRendererDrawScanline,
 31	.finishFrame = GBVideoDummyRendererFinishFrame,
 32	.getPixels = GBVideoDummyRendererGetPixels
 33};
 34
 35void GBVideoInit(struct GBVideo* video) {
 36	video->renderer = &dummyRenderer;
 37	video->vram = 0;
 38	video->frameskip = 0;
 39}
 40
 41void GBVideoReset(struct GBVideo* video) {
 42	video->ly = 0;
 43	video->mode = 1;
 44	video->stat = 1;
 45
 46	video->nextEvent = INT_MAX;
 47	video->eventDiff = 0;
 48
 49	video->nextMode = INT_MAX;
 50
 51	video->frameCounter = 0;
 52	video->frameskipCounter = 0;
 53
 54	if (video->vram) {
 55		mappedMemoryFree(video->vram, GB_SIZE_VRAM);
 56	}
 57	video->vram = anonymousMemoryMap(GB_SIZE_VRAM);
 58	video->renderer->vram = video->vram;
 59	memset(&video->oam, 0, sizeof(video->oam));
 60	video->renderer->oam = &video->oam;
 61
 62	video->renderer->deinit(video->renderer);
 63	video->renderer->init(video->renderer);
 64}
 65
 66void GBVideoDeinit(struct GBVideo* video) {
 67	GBVideoAssociateRenderer(video, &dummyRenderer);
 68	mappedMemoryFree(video->vram, GB_SIZE_VRAM);
 69}
 70
 71void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer) {
 72	video->renderer->deinit(video->renderer);
 73	video->renderer = renderer;
 74	renderer->vram = video->vram;
 75	video->renderer->init(video->renderer);
 76}
 77
 78int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
 79	video->eventDiff += cycles;
 80	if (video->nextEvent != INT_MAX) {
 81		video->nextEvent -= cycles;
 82	}
 83	if (video->nextEvent <= 0) {
 84		if (video->nextEvent != INT_MAX) {
 85			video->nextMode -= video->eventDiff;
 86		}
 87		if (video->nextMode <= 0) {
 88			int lyc = video->p->memory.io[REG_LYC];
 89			switch (video->mode) {
 90			case 0:
 91				++video->ly;
 92				video->p->memory.io[REG_LY] = video->ly;
 93				video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
 94				if (GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) {
 95					video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
 96				}
 97				if (video->ly < GB_VIDEO_VERTICAL_PIXELS) {
 98					video->renderer->drawScanline(video->renderer, video->ly);
 99					video->nextMode = GB_VIDEO_MODE_2_LENGTH;
100					video->mode = 2;
101					if (GBRegisterSTATIsOAMIRQ(video->stat)) {
102						video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
103					}
104				} else {
105					video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH;
106					video->mode = 1;
107					++video->frameCounter;
108					video->renderer->finishFrame(video->renderer);
109					if (GBRegisterSTATIsVblankIRQ(video->stat)) {
110						video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
111					}
112					video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
113				}
114				GBUpdateIRQs(video->p);
115				break;
116			case 1:
117				++video->ly;
118				video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
119				if (GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) {
120					video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
121				}
122				if (video->ly >= GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
123					video->ly = 0;
124					video->renderer->drawScanline(video->renderer, video->ly);
125					video->nextMode = GB_VIDEO_MODE_2_LENGTH;
126					video->mode = 2;
127					if (GBRegisterSTATIsOAMIRQ(video->stat)) {
128						video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
129						GBUpdateIRQs(video->p);
130					}
131				} else {
132					video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH;
133				}
134				video->p->memory.io[REG_LY] = video->ly;
135				break;
136			case 2:
137				video->nextMode = GB_VIDEO_MODE_3_LENGTH;
138				video->mode = 3;
139				break;
140			case 3:
141				video->nextMode = GB_VIDEO_MODE_0_LENGTH;
142				video->mode = 0;
143				if (GBRegisterSTATIsHblankIRQ(video->stat)) {
144					video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
145					GBUpdateIRQs(video->p);
146				}
147				break;
148			}
149			video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
150			video->p->memory.io[REG_STAT] = video->stat;
151		}
152
153		video->nextEvent = video->nextMode;
154		video->eventDiff = 0;
155	}
156	return video->nextEvent;
157}
158
159void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
160	if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && GBRegisterLCDCIsEnable(value)) {
161		// TODO: Does enabling the LCD start in vblank?
162		video->mode = 2;
163		video->nextMode = GB_VIDEO_MODE_2_LENGTH;
164		video->nextEvent = video->nextMode;
165		video->eventDiff = 0;
166		video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
167		video->p->memory.io[REG_STAT] = video->stat;
168		video->eventDiff = 0;
169		if (video->p->cpu->cycles + video->nextEvent < video->p->cpu->nextEvent) {
170			video->p->cpu->nextEvent = video->p->cpu->cycles + video->nextEvent;
171		}
172		return;
173	}
174	if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
175		video->mode = 0;
176		video->nextMode = INT_MAX;
177		video->nextEvent = INT_MAX;
178		video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
179		video->p->memory.io[REG_STAT] = video->stat;
180		video->ly = 0;
181		video->p->memory.io[REG_LY] = 0;
182	}
183}
184
185void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
186	video->stat = (video->stat & 0x7) | (value & 0x78);
187}
188
189static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer) {
190	UNUSED(renderer);
191	// Nothing to do
192}
193
194static void GBVideoDummyRendererReset(struct GBVideoRenderer* renderer) {
195	UNUSED(renderer);
196	// Nothing to do
197}
198
199static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer) {
200	UNUSED(renderer);
201	// Nothing to do
202}
203
204static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
205	UNUSED(renderer);
206	UNUSED(address);
207	return value;
208}
209
210static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
211	UNUSED(renderer);
212	UNUSED(address);
213	// Nothing to do
214}
215
216static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint8_t oam) {
217	UNUSED(renderer);
218	UNUSED(oam);
219	// Nothing to do
220}
221
222static void GBVideoDummyRendererDrawScanline(struct GBVideoRenderer* renderer, int y) {
223	UNUSED(renderer);
224	UNUSED(y);
225	// Nothing to do
226}
227
228static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer) {
229	UNUSED(renderer);
230	// Nothing to do
231}
232
233static void GBVideoDummyRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels) {
234	UNUSED(renderer);
235	UNUSED(stride);
236	UNUSED(pixels);
237	// Nothing to do
238}