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 GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
16static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer);
17
18static struct GBAVideoRenderer dummyRenderer = {
19 .init = GBAVideoDummyRendererInit,
20 .reset = GBAVideoDummyRendererReset,
21 .deinit = GBAVideoDummyRendererDeinit,
22 .writeVideoRegister = GBAVideoDummyRendererWriteVideoRegister,
23 .drawScanline = GBAVideoDummyRendererDrawScanline,
24 .finishFrame = GBAVideoDummyRendererFinishFrame
25};
26
27void GBAVideoInit(struct GBAVideo* video) {
28 video->renderer = &dummyRenderer;
29 video->vram = 0;
30}
31
32void GBAVideoReset(struct GBAVideo* video) {
33 video->inHblank = 0;
34 video->inVblank = 0;
35 video->vcounter = 0;
36 video->vblankIRQ = 0;
37 video->hblankIRQ = 0;
38 video->vcounterIRQ = 0;
39 video->vcountSetting = 0;
40
41 video->vcount = 0;
42
43 video->lastHblank = 0;
44 video->nextHblank = VIDEO_HDRAW_LENGTH;
45 video->nextEvent = video->nextHblank;
46 video->eventDiff = video->nextEvent;
47
48 video->nextHblankIRQ = 0;
49 video->nextVblankIRQ = 0;
50 video->nextVcounterIRQ = 0;
51
52 if (video->vram) {
53 mappedMemoryFree(video->vram, SIZE_VRAM);
54 }
55 video->vram = anonymousMemoryMap(SIZE_VRAM);
56 video->renderer->vram = video->vram;
57
58 int i;
59 for (i = 0; i < 128; ++i) {
60 video->oam.raw[i * 4] = 0x0200;
61 video->oam.raw[i * 4 + 1] = 0x0000;
62 video->oam.raw[i * 4 + 2] = 0x0000;
63 video->oam.raw[i * 4 + 3] = 0x0000;
64 }
65}
66
67void GBAVideoDeinit(struct GBAVideo* video) {
68 GBAVideoAssociateRenderer(video, &dummyRenderer);
69 mappedMemoryFree(video->vram, SIZE_VRAM);
70}
71
72void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer* renderer) {
73 video->renderer->deinit(video->renderer);
74 video->renderer = renderer;
75 renderer->palette = video->palette;
76 renderer->vram = video->vram;
77 renderer->oam = &video->oam;
78 video->renderer->init(video->renderer);
79}
80
81int32_t GBAVideoProcessEvents(struct GBAVideo* video, int32_t cycles) {
82 video->nextEvent -= cycles;
83 video->eventDiff += cycles;
84 if (video->nextEvent <= 0) {
85 int32_t lastEvent = video->nextEvent;
86 video->lastHblank -= video->eventDiff;
87 video->nextHblank -= video->eventDiff;
88 video->nextHblankIRQ -= video->eventDiff;
89 video->nextVcounterIRQ -= video->eventDiff;
90
91 if (video->inHblank) {
92 // End Hblank
93 video->inHblank = 0;
94 video->nextEvent = video->nextHblank;
95
96 ++video->vcount;
97 video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
98
99 switch (video->vcount) {
100 case VIDEO_VERTICAL_PIXELS:
101 video->inVblank = 1;
102 if (GBASyncDrawingFrame(video->p->sync)) {
103 video->renderer->finishFrame(video->renderer);
104 }
105 video->nextVblankIRQ = video->nextEvent + VIDEO_TOTAL_LENGTH;
106 GBAMemoryRunVblankDMAs(video->p, lastEvent);
107 if (video->vblankIRQ) {
108 GBARaiseIRQ(video->p, IRQ_VBLANK);
109 }
110 GBASyncPostFrame(video->p->sync);
111 break;
112 case VIDEO_VERTICAL_TOTAL_PIXELS - 1:
113 if (video->p->rr) {
114 GBARRNextFrame(video->p->rr);
115 }
116 video->inVblank = 0;
117 break;
118 case VIDEO_VERTICAL_TOTAL_PIXELS:
119 video->vcount = 0;
120 video->p->memory.io[REG_VCOUNT >> 1] = 0;
121 break;
122 }
123
124 video->vcounter = video->vcount == video->vcountSetting;
125 if (video->vcounter && video->vcounterIRQ) {
126 GBARaiseIRQ(video->p, IRQ_VCOUNTER);
127 video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
128 }
129 } else {
130 // Begin Hblank
131 video->inHblank = 1;
132 video->lastHblank = video->nextHblank;
133 video->nextEvent = video->lastHblank + VIDEO_HBLANK_LENGTH;
134 video->nextHblank = video->nextEvent + VIDEO_HDRAW_LENGTH;
135 video->nextHblankIRQ = video->nextHblank;
136
137 if (video->vcount < VIDEO_VERTICAL_PIXELS && GBASyncDrawingFrame(video->p->sync)) {
138 video->renderer->drawScanline(video->renderer, video->vcount);
139 }
140
141 if (video->vcount < VIDEO_VERTICAL_PIXELS) {
142 GBAMemoryRunHblankDMAs(video->p, lastEvent);
143 }
144 if (video->hblankIRQ) {
145 GBARaiseIRQ(video->p, IRQ_HBLANK);
146 }
147 }
148
149 video->eventDiff = 0;
150 }
151 video->p->memory.io[REG_DISPSTAT >> 1] &= 0xFFF8;
152 video->p->memory.io[REG_DISPSTAT >> 1] |= (video->inVblank) | (video->inHblank << 1) | (video->vcounter << 2);
153 return video->nextEvent;
154}
155
156void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value) {
157 union GBARegisterDISPSTAT dispstat;
158 dispstat.packed = value;
159 video->vblankIRQ = dispstat.vblankIRQ;
160 video->hblankIRQ = dispstat.hblankIRQ;
161 video->vcounterIRQ = dispstat.vcounterIRQ;
162 video->vcountSetting = dispstat.vcountSetting;
163
164 if (video->vcounterIRQ) {
165 // FIXME: this can be too late if we're in the middle of an Hblank
166 video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (video->vcountSetting - video->vcount) * VIDEO_HORIZONTAL_LENGTH;
167 if (video->nextVcounterIRQ < video->nextEvent) {
168 video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
169 }
170 }
171}
172
173static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer) {
174 UNUSED(renderer);
175 // Nothing to do
176}
177
178static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer) {
179 UNUSED(renderer);
180 // Nothing to do
181}
182
183static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer) {
184 UNUSED(renderer);
185 // Nothing to do
186}
187
188static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
189 UNUSED(renderer);
190 UNUSED(address);
191 return value;
192}
193
194static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
195 UNUSED(renderer);
196 UNUSED(y);
197 // Nothing to do
198}
199
200static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
201 UNUSED(renderer);
202 // Nothing to do
203}
204
205void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state) {
206 memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
207 memcpy(state->oam, video->oam.raw, SIZE_OAM);
208 memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
209 union GBARegisterDISPSTAT dispstat;
210 dispstat.inVblank = video->inVblank;
211 dispstat.inHblank = video->inHblank;
212 dispstat.vcounter = video->vcounter;
213 dispstat.vblankIRQ = video->vblankIRQ;
214 dispstat.hblankIRQ = video->hblankIRQ;
215 dispstat.vcounterIRQ = video->vcounterIRQ;
216 dispstat.vcountSetting = video->vcountSetting;
217 state->io[REG_DISPSTAT >> 1] = dispstat.packed;
218 state->video.nextEvent = video->nextEvent;
219 state->video.eventDiff = video->eventDiff;
220 state->video.lastHblank = video->lastHblank;
221 state->video.nextHblank = video->nextHblank;
222 state->video.nextHblankIRQ = video->nextHblankIRQ;
223 state->video.nextVblankIRQ = video->nextVblankIRQ;
224 state->video.nextVcounterIRQ = video->nextVcounterIRQ;
225}
226
227void GBAVideoDeserialize(struct GBAVideo* video, struct GBASerializedState* state) {
228 memcpy(video->renderer->vram, state->vram, SIZE_VRAM);
229 int i;
230 for (i = 0; i < SIZE_OAM; i += 2) {
231 GBAStore16(video->p->cpu, BASE_OAM | i, state->oam[i >> 1], 0);
232 }
233 for (i = 0; i < SIZE_PALETTE_RAM; i += 2) {
234 GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, state->pram[i >> 1], 0);
235 }
236 union GBARegisterDISPSTAT dispstat;
237 dispstat.packed = state->io[REG_DISPSTAT >> 1];
238 video->inVblank = dispstat.inVblank;
239 video->inHblank = dispstat.inHblank;
240 video->vcounter = dispstat.vcounter;
241 video->vblankIRQ = dispstat.vblankIRQ;
242 video->hblankIRQ = dispstat.hblankIRQ;
243 video->vcounterIRQ = dispstat.vcounterIRQ;
244 video->vcountSetting = dispstat.vcountSetting;
245 video->nextEvent = state->video.nextEvent;
246 video->eventDiff = state->video.eventDiff;
247 video->lastHblank = state->video.lastHblank;
248 video->nextHblank = state->video.nextHblank;
249 video->nextHblankIRQ = state->video.nextHblankIRQ;
250 video->nextVblankIRQ = state->video.nextVblankIRQ;
251 video->nextVcounterIRQ = state->video.nextVcounterIRQ;
252 video->vcount = state->io[REG_VCOUNT >> 1];
253}