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 = -1;
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 GBARRNextFrame(video->p);
114 video->inVblank = 0;
115 break;
116 case VIDEO_VERTICAL_TOTAL_PIXELS:
117 video->vcount = 0;
118 video->p->memory.io[REG_VCOUNT >> 1] = 0;
119 break;
120 }
121
122 video->vcounter = video->vcount == video->vcountSetting;
123 if (video->vcounter && video->vcounterIRQ) {
124 GBARaiseIRQ(video->p, IRQ_VCOUNTER);
125 video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
126 }
127
128 if (video->vcount < VIDEO_VERTICAL_PIXELS && GBASyncDrawingFrame(video->p->sync)) {
129 video->renderer->drawScanline(video->renderer, video->vcount);
130 }
131 } else {
132 // Begin Hblank
133 video->inHblank = 1;
134 video->lastHblank = video->nextHblank;
135 video->nextEvent = video->lastHblank + VIDEO_HBLANK_LENGTH;
136 video->nextHblank = video->nextEvent + VIDEO_HDRAW_LENGTH;
137 video->nextHblankIRQ = video->nextHblank;
138
139 if (video->vcount < VIDEO_VERTICAL_PIXELS) {
140 GBAMemoryRunHblankDMAs(video->p, lastEvent);
141 }
142 if (video->hblankIRQ) {
143 GBARaiseIRQ(video->p, IRQ_HBLANK);
144 }
145 }
146
147 video->eventDiff = 0;
148 }
149 video->p->memory.io[REG_DISPSTAT >> 1] &= 0xFFF8;
150 video->p->memory.io[REG_DISPSTAT >> 1] |= (video->inVblank) | (video->inHblank << 1) | (video->vcounter << 2);
151 return video->nextEvent;
152}
153
154void GBAVideoWriteDISPSTAT(struct GBAVideo* video, uint16_t value) {
155 union GBARegisterDISPSTAT dispstat;
156 dispstat.packed = value;
157 video->vblankIRQ = dispstat.vblankIRQ;
158 video->hblankIRQ = dispstat.hblankIRQ;
159 video->vcounterIRQ = dispstat.vcounterIRQ;
160 video->vcountSetting = dispstat.vcountSetting;
161
162 if (video->vcounterIRQ) {
163 // FIXME: this can be too late if we're in the middle of an Hblank
164 video->nextVcounterIRQ = video->nextHblank + VIDEO_HBLANK_LENGTH + (video->vcountSetting - video->vcount) * VIDEO_HORIZONTAL_LENGTH;
165 if (video->nextVcounterIRQ < video->nextEvent) {
166 video->nextVcounterIRQ += VIDEO_TOTAL_LENGTH;
167 }
168 }
169}
170
171static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer) {
172 UNUSED(renderer);
173 // Nothing to do
174}
175
176static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer) {
177 UNUSED(renderer);
178 // Nothing to do
179}
180
181static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer) {
182 UNUSED(renderer);
183 // Nothing to do
184}
185
186static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
187 UNUSED(renderer);
188 UNUSED(address);
189 return value;
190}
191
192static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
193 UNUSED(renderer);
194 UNUSED(y);
195 // Nothing to do
196}
197
198static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
199 UNUSED(renderer);
200 // Nothing to do
201}
202
203void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state) {
204 memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
205 memcpy(state->oam, video->oam.raw, SIZE_OAM);
206 memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
207 union GBARegisterDISPSTAT dispstat;
208 dispstat.inVblank = video->inVblank;
209 dispstat.inHblank = video->inHblank;
210 dispstat.vcounter = video->vcounter;
211 dispstat.vblankIRQ = video->vblankIRQ;
212 dispstat.hblankIRQ = video->hblankIRQ;
213 dispstat.vcounterIRQ = video->vcounterIRQ;
214 dispstat.vcountSetting = video->vcountSetting;
215 state->io[REG_DISPSTAT >> 1] = dispstat.packed;
216 state->video.nextEvent = video->nextEvent;
217 state->video.eventDiff = video->eventDiff;
218 state->video.lastHblank = video->lastHblank;
219 state->video.nextHblank = video->nextHblank;
220 state->video.nextHblankIRQ = video->nextHblankIRQ;
221 state->video.nextVblankIRQ = video->nextVblankIRQ;
222 state->video.nextVcounterIRQ = video->nextVcounterIRQ;
223}
224
225void GBAVideoDeserialize(struct GBAVideo* video, struct GBASerializedState* state) {
226 memcpy(video->renderer->vram, state->vram, SIZE_VRAM);
227 int i;
228 for (i = 0; i < SIZE_OAM; i += 2) {
229 GBAStore16(video->p->cpu, BASE_OAM | i, state->oam[i >> 1], 0);
230 }
231 for (i = 0; i < SIZE_PALETTE_RAM; i += 2) {
232 GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, state->pram[i >> 1], 0);
233 }
234 union GBARegisterDISPSTAT dispstat;
235 dispstat.packed = state->io[REG_DISPSTAT >> 1];
236 video->inVblank = dispstat.inVblank;
237 video->inHblank = dispstat.inHblank;
238 video->vcounter = dispstat.vcounter;
239 video->vblankIRQ = dispstat.vblankIRQ;
240 video->hblankIRQ = dispstat.hblankIRQ;
241 video->vcounterIRQ = dispstat.vcounterIRQ;
242 video->vcountSetting = dispstat.vcountSetting;
243 video->nextEvent = state->video.nextEvent;
244 video->eventDiff = state->video.eventDiff;
245 video->lastHblank = state->video.lastHblank;
246 video->nextHblank = state->video.nextHblank;
247 video->nextHblankIRQ = state->video.nextHblankIRQ;
248 video->nextVblankIRQ = state->video.nextVblankIRQ;
249 video->nextVcounterIRQ = state->video.nextVcounterIRQ;
250 video->vcount = state->io[REG_VCOUNT >> 1];
251}