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