all repos — mgba @ b9e6ac954d606130f968878138163e304fdaa4c4

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			switch (video->mode) {
 89			case 0:
 90				++video->ly;
 91				video->p->memory.io[REG_LY] = video->ly;
 92				int lyc = video->p->memory.io[REG_LYC];
 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				if (video->ly >= GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
119					video->ly = 0;
120					video->renderer->drawScanline(video->renderer, video->ly);
121					video->nextMode = GB_VIDEO_MODE_2_LENGTH;
122					video->mode = 2;
123					if (GBRegisterSTATIsOAMIRQ(video->stat)) {
124						video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
125						GBUpdateIRQs(video->p);
126					}
127				} else {
128					video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH;
129				}
130				video->p->memory.io[REG_LY] = video->ly;
131				break;
132			case 2:
133				video->nextMode = GB_VIDEO_MODE_3_LENGTH;
134				video->mode = 3;
135				break;
136			case 3:
137				video->nextMode = GB_VIDEO_MODE_0_LENGTH;
138				video->mode = 0;
139				if (GBRegisterSTATIsHblankIRQ(video->stat)) {
140					video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
141					GBUpdateIRQs(video->p);
142				}
143				break;
144			}
145			video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
146			video->p->memory.io[REG_STAT] = video->stat;
147		}
148
149		video->nextEvent = video->nextMode;
150		video->eventDiff = 0;
151	}
152	return video->nextEvent;
153}
154
155void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
156	if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && GBRegisterLCDCIsEnable(value)) {
157		// TODO: Does enabling the LCD start in vblank?
158		video->mode = 2;
159		video->nextMode = GB_VIDEO_MODE_2_LENGTH;
160		video->nextEvent = video->nextMode;
161		video->eventDiff = 0;
162		video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
163		video->p->memory.io[REG_STAT] = video->stat;
164		video->eventDiff = 0;
165		if (video->p->cpu->cycles + video->nextEvent < video->p->cpu->nextEvent) {
166			video->p->cpu->nextEvent = video->p->cpu->cycles + video->nextEvent;
167		}
168		return;
169	}
170	if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
171		video->mode = 0;
172		video->nextMode = INT_MAX;
173		video->nextEvent = INT_MAX;
174		video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
175		video->p->memory.io[REG_STAT] = video->stat;
176		video->ly = 0;
177		video->p->memory.io[REG_LY] = 0;
178	}
179}
180
181void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
182	video->stat = (video->stat & 0x7) | (value & 0x78);
183}
184
185static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer) {
186	UNUSED(renderer);
187	// Nothing to do
188}
189
190static void GBVideoDummyRendererReset(struct GBVideoRenderer* renderer) {
191	UNUSED(renderer);
192	// Nothing to do
193}
194
195static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer) {
196	UNUSED(renderer);
197	// Nothing to do
198}
199
200static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
201	UNUSED(renderer);
202	UNUSED(address);
203	return value;
204}
205
206static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
207	UNUSED(renderer);
208	UNUSED(address);
209	// Nothing to do
210}
211
212static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint8_t oam) {
213	UNUSED(renderer);
214	UNUSED(oam);
215	// Nothing to do
216}
217
218static void GBVideoDummyRendererDrawScanline(struct GBVideoRenderer* renderer, int y) {
219	UNUSED(renderer);
220	UNUSED(y);
221	// Nothing to do
222}
223
224static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer) {
225	UNUSED(renderer);
226	// Nothing to do
227}
228
229static void GBVideoDummyRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels) {
230	UNUSED(renderer);
231	UNUSED(stride);
232	UNUSED(pixels);
233	// Nothing to do
234}