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