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 <mgba/internal/gb/video.h>
7
8#include <mgba/core/sync.h>
9#include <mgba/core/thread.h>
10#include <mgba/core/cache-set.h>
11#include <mgba/internal/gb/gb.h>
12#include <mgba/internal/gb/io.h>
13#include <mgba/internal/gb/renderers/cache-set.h>
14#include <mgba/internal/gb/serialize.h>
15#include <mgba/internal/lr35902/lr35902.h>
16
17#include <mgba-util/memory.h>
18
19static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders);
20static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
21static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
22static void GBVideoDummyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
23static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
24static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
25static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
26static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax);
27static void GBVideoDummyRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
28static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer);
29static void GBVideoDummyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels);
30static void GBVideoDummyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels);
31
32static void _cleanOAM(struct GBVideo* video, int y);
33
34static void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate);
35static void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate);
36static void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate);
37static void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate);
38static void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLate);
39
40static struct GBVideoRenderer dummyRenderer = {
41 .init = GBVideoDummyRendererInit,
42 .deinit = GBVideoDummyRendererDeinit,
43 .writeVideoRegister = GBVideoDummyRendererWriteVideoRegister,
44 .writeSGBPacket = GBVideoDummyRendererWriteSGBPacket,
45 .writeVRAM = GBVideoDummyRendererWriteVRAM,
46 .writeOAM = GBVideoDummyRendererWriteOAM,
47 .writePalette = GBVideoDummyRendererWritePalette,
48 .drawRange = GBVideoDummyRendererDrawRange,
49 .finishScanline = GBVideoDummyRendererFinishScanline,
50 .finishFrame = GBVideoDummyRendererFinishFrame,
51 .getPixels = GBVideoDummyRendererGetPixels,
52 .putPixels = GBVideoDummyRendererPutPixels,
53};
54
55void GBVideoInit(struct GBVideo* video) {
56 video->renderer = &dummyRenderer;
57 video->renderer->cache = NULL;
58 video->renderer->sgbRenderMode = 0;
59 video->vram = anonymousMemoryMap(GB_SIZE_VRAM);
60 video->frameskip = 0;
61
62 video->modeEvent.context = video;
63 video->modeEvent.name = "GB Video Mode";
64 video->modeEvent.callback = NULL;
65 video->modeEvent.priority = 8;
66 video->frameEvent.context = video;
67 video->frameEvent.name = "GB Video Frame";
68 video->frameEvent.callback = _updateFrameCount;
69 video->frameEvent.priority = 9;
70
71 video->dmgPalette[0] = 0x7FFF;
72 video->dmgPalette[1] = 0x56B5;
73 video->dmgPalette[2] = 0x294A;
74 video->dmgPalette[3] = 0x0000;
75 video->dmgPalette[4] = 0x7FFF;
76 video->dmgPalette[5] = 0x56B5;
77 video->dmgPalette[6] = 0x294A;
78 video->dmgPalette[7] = 0x0000;
79 video->dmgPalette[8] = 0x7FFF;
80 video->dmgPalette[9] = 0x56B5;
81 video->dmgPalette[10] = 0x294A;
82 video->dmgPalette[11] = 0x0000;
83
84 video->sgbBorders = true;
85
86 video->renderer->sgbCharRam = NULL;
87 video->renderer->sgbMapRam = NULL;
88 video->renderer->sgbPalRam = NULL;
89 video->renderer->sgbAttributes = NULL;
90 video->renderer->sgbAttributeFiles = NULL;
91}
92
93void GBVideoReset(struct GBVideo* video) {
94 video->ly = 0;
95 video->x = 0;
96 video->mode = 1;
97 video->stat = 1;
98
99 video->frameCounter = 0;
100 video->frameskipCounter = 0;
101
102 GBVideoSwitchBank(video, 0);
103 video->renderer->vram = video->vram;
104 memset(&video->oam, 0, sizeof(video->oam));
105 video->renderer->oam = &video->oam;
106 memset(&video->palette, 0, sizeof(video->palette));
107
108 if (video->p->model == GB_MODEL_SGB) {
109 video->renderer->sgbCharRam = anonymousMemoryMap(SGB_SIZE_CHAR_RAM);
110 video->renderer->sgbMapRam = anonymousMemoryMap(SGB_SIZE_MAP_RAM);
111 video->renderer->sgbPalRam = anonymousMemoryMap(SGB_SIZE_PAL_RAM);
112 video->renderer->sgbAttributeFiles = anonymousMemoryMap(SGB_SIZE_ATF_RAM);
113 video->renderer->sgbAttributes = malloc(90 * 45);
114 memset(video->renderer->sgbAttributes, 0, 90 * 45);
115 video->sgbCommandHeader = 0;
116 }
117
118 video->palette[0] = video->dmgPalette[0];
119 video->palette[1] = video->dmgPalette[1];
120 video->palette[2] = video->dmgPalette[2];
121 video->palette[3] = video->dmgPalette[3];
122 video->palette[8 * 4 + 0] = video->dmgPalette[4];
123 video->palette[8 * 4 + 1] = video->dmgPalette[5];
124 video->palette[8 * 4 + 2] = video->dmgPalette[6];
125 video->palette[8 * 4 + 3] = video->dmgPalette[7];
126 video->palette[9 * 4 + 0] = video->dmgPalette[8];
127 video->palette[9 * 4 + 1] = video->dmgPalette[9];
128 video->palette[9 * 4 + 2] = video->dmgPalette[10];
129 video->palette[9 * 4 + 3] = video->dmgPalette[11];
130
131 video->renderer->deinit(video->renderer);
132 video->renderer->init(video->renderer, video->p->model, video->sgbBorders);
133
134 video->renderer->writePalette(video->renderer, 0, video->palette[0]);
135 video->renderer->writePalette(video->renderer, 1, video->palette[1]);
136 video->renderer->writePalette(video->renderer, 2, video->palette[2]);
137 video->renderer->writePalette(video->renderer, 3, video->palette[3]);
138 video->renderer->writePalette(video->renderer, 8 * 4 + 0, video->palette[8 * 4 + 0]);
139 video->renderer->writePalette(video->renderer, 8 * 4 + 1, video->palette[8 * 4 + 1]);
140 video->renderer->writePalette(video->renderer, 8 * 4 + 2, video->palette[8 * 4 + 2]);
141 video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]);
142 video->renderer->writePalette(video->renderer, 9 * 4 + 0, video->palette[9 * 4 + 0]);
143 video->renderer->writePalette(video->renderer, 9 * 4 + 1, video->palette[9 * 4 + 1]);
144 video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]);
145 video->renderer->writePalette(video->renderer, 9 * 4 + 3, video->palette[9 * 4 + 3]);
146}
147
148void GBVideoDeinit(struct GBVideo* video) {
149 GBVideoAssociateRenderer(video, &dummyRenderer);
150 mappedMemoryFree(video->vram, GB_SIZE_VRAM);
151 if (video->renderer->sgbCharRam) {
152 mappedMemoryFree(video->renderer->sgbCharRam, SGB_SIZE_CHAR_RAM);
153 video->renderer->sgbCharRam = NULL;
154 }
155 if (video->renderer->sgbMapRam) {
156 mappedMemoryFree(video->renderer->sgbMapRam, SGB_SIZE_MAP_RAM);
157 video->renderer->sgbMapRam = NULL;
158 }
159 if (video->renderer->sgbPalRam) {
160 mappedMemoryFree(video->renderer->sgbPalRam, SGB_SIZE_PAL_RAM);
161 video->renderer->sgbPalRam = NULL;
162 }
163 if (video->renderer->sgbAttributeFiles) {
164 mappedMemoryFree(video->renderer->sgbAttributeFiles, SGB_SIZE_ATF_RAM);
165 video->renderer->sgbAttributeFiles = NULL;
166 }
167 if (video->renderer->sgbAttributes) {
168 free(video->renderer->sgbAttributes);
169 video->renderer->sgbAttributes = NULL;
170 }
171}
172
173void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer) {
174 video->renderer->deinit(video->renderer);
175 renderer->cache = video->renderer->cache;
176 renderer->sgbRenderMode = video->renderer->sgbRenderMode;
177 renderer->sgbCharRam = video->renderer->sgbCharRam;
178 renderer->sgbMapRam = video->renderer->sgbMapRam;
179 renderer->sgbPalRam = video->renderer->sgbPalRam;
180 renderer->sgbAttributeFiles = video->renderer->sgbAttributeFiles;
181 renderer->sgbAttributes = video->renderer->sgbAttributes;
182 video->renderer = renderer;
183 renderer->vram = video->vram;
184 video->renderer->init(video->renderer, video->p->model, video->sgbBorders);
185}
186
187static bool _statIRQAsserted(struct GBVideo* video, GBRegisterSTAT stat) {
188 // TODO: variable for the IRQ line value?
189 if (GBRegisterSTATIsLYCIRQ(stat) && GBRegisterSTATIsLYC(stat)) {
190 return true;
191 }
192 switch (GBRegisterSTATGetMode(stat)) {
193 case 0:
194 if (GBRegisterSTATIsHblankIRQ(stat)) {
195 return true;
196 }
197 break;
198 case 1:
199 if (GBRegisterSTATIsVblankIRQ(stat)) {
200 return true;
201 }
202 break;
203 case 2:
204 if (GBRegisterSTATIsOAMIRQ(stat)) {
205 return true;
206 }
207 break;
208 case 3:
209 break;
210 }
211 return false;
212}
213
214void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
215 struct GBVideo* video = context;
216 if (video->frameskipCounter <= 0) {
217 video->renderer->finishScanline(video->renderer, video->ly);
218 }
219 int lyc = video->p->memory.io[REG_LYC];
220 int32_t next;
221 ++video->ly;
222 video->p->memory.io[REG_LY] = video->ly;
223 GBRegisterSTAT oldStat = video->stat;
224 video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
225 if (video->ly < GB_VIDEO_VERTICAL_PIXELS) {
226 // TODO: Cache SCX & 7 in case it changes during mode 2
227 next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
228 video->mode = 2;
229 video->modeEvent.callback = _endMode2;
230 } else {
231 next = GB_VIDEO_HORIZONTAL_LENGTH;
232 video->mode = 1;
233 video->modeEvent.callback = _endMode1;
234
235 mTimingDeschedule(&video->p->timing, &video->frameEvent);
236 mTimingSchedule(&video->p->timing, &video->frameEvent, -cyclesLate);
237
238 if (!_statIRQAsserted(video, oldStat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
239 video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
240 }
241 video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
242 }
243 video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
244 if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
245 video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
246 }
247 GBUpdateIRQs(video->p);
248 video->p->memory.io[REG_STAT] = video->stat;
249 mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
250}
251
252void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
253 struct GBVideo* video = context;
254 if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
255 return;
256 }
257 int lyc = video->p->memory.io[REG_LYC];
258 // TODO: One M-cycle delay
259 ++video->ly;
260 int32_t next;
261 if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS + 1) {
262 video->ly = 0;
263 video->p->memory.io[REG_LY] = video->ly;
264 next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
265 video->mode = 2;
266 video->modeEvent.callback = _endMode2;
267 } else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
268 video->p->memory.io[REG_LY] = 0;
269 next = GB_VIDEO_HORIZONTAL_LENGTH - 8;
270 } else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS - 1) {
271 video->p->memory.io[REG_LY] = video->ly;
272 next = 8;
273 } else {
274 video->p->memory.io[REG_LY] = video->ly;
275 next = GB_VIDEO_HORIZONTAL_LENGTH;
276 }
277
278 GBRegisterSTAT oldStat = video->stat;
279 video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
280 video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]);
281 if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
282 video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
283 GBUpdateIRQs(video->p);
284 }
285 video->p->memory.io[REG_STAT] = video->stat;
286 mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
287}
288
289void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
290 struct GBVideo* video = context;
291 _cleanOAM(video, video->ly);
292 video->x = 0;
293 video->dotClock = mTimingCurrentTime(timing) - cyclesLate;
294 int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 6 - (video->p->memory.io[REG_SCX] & 7);
295 video->mode = 3;
296 video->modeEvent.callback = _endMode3;
297 GBRegisterSTAT oldStat = video->stat;
298 video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
299 if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
300 video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
301 GBUpdateIRQs(video->p);
302 }
303 video->p->memory.io[REG_STAT] = video->stat;
304 mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
305}
306
307void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
308 struct GBVideo* video = context;
309 GBVideoProcessDots(video, cyclesLate);
310 if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) {
311 video->p->memory.hdmaRemaining = 0x10;
312 video->p->cpuBlocked = true;
313 mTimingDeschedule(timing, &video->p->memory.hdmaEvent);
314 mTimingSchedule(timing, &video->p->memory.hdmaEvent, 0);
315 }
316 video->mode = 0;
317 video->modeEvent.callback = _endMode0;
318 GBRegisterSTAT oldStat = video->stat;
319 video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
320 if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
321 video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
322 GBUpdateIRQs(video->p);
323 }
324 video->p->memory.io[REG_STAT] = video->stat;
325 int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 6;
326 mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
327}
328
329void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLate) {
330 UNUSED(cyclesLate);
331 struct GBVideo* video = context;
332 if (video->p->cpu->executionState != LR35902_CORE_FETCH) {
333 mTimingSchedule(timing, &video->frameEvent, 4 - ((video->p->cpu->executionState + 1) & 3));
334 return;
335 }
336
337 size_t c;
338 for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
339 struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
340 if (callbacks->videoFrameEnded) {
341 callbacks->videoFrameEnded(callbacks->context);
342 }
343 }
344
345 GBFrameEnded(video->p);
346 mCoreSyncPostFrame(video->p->sync);
347 --video->frameskipCounter;
348 if (video->frameskipCounter < 0) {
349 video->renderer->finishFrame(video->renderer);
350 video->frameskipCounter = video->frameskip;
351 }
352 ++video->frameCounter;
353
354 // TODO: Move to common code
355 if (video->p->stream && video->p->stream->postVideoFrame) {
356 const color_t* pixels;
357 size_t stride;
358 video->renderer->getPixels(video->renderer, &stride, (const void**) &pixels);
359 video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
360 }
361
362 if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
363 mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
364 }
365
366 for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
367 struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
368 if (callbacks->videoFrameStarted) {
369 callbacks->videoFrameStarted(callbacks->context);
370 }
371 }
372}
373
374static void _cleanOAM(struct GBVideo* video, int y) {
375 // TODO: GBC differences
376 // TODO: Optimize
377 video->objMax = 0;
378 int spriteHeight = 8;
379 if (GBRegisterLCDCIsObjSize(video->p->memory.io[REG_LCDC])) {
380 spriteHeight = 16;
381 }
382 int o = 0;
383 int i;
384 for (i = 0; i < 40; ++i) {
385 uint8_t oy = video->oam.obj[i].y;
386 if (y < oy - 16 || y >= oy - 16 + spriteHeight) {
387 continue;
388 }
389 // TODO: Sort
390 video->objThisLine[o] = video->oam.obj[i];
391 ++o;
392 if (o == 10) {
393 break;
394 }
395 }
396 video->objMax = o;
397}
398
399void GBVideoProcessDots(struct GBVideo* video, uint32_t cyclesLate) {
400 if (video->mode != 3) {
401 return;
402 }
403 int oldX = video->x;
404 video->x = (mTimingCurrentTime(&video->p->timing) - video->dotClock - cyclesLate) >> video->p->doubleSpeed;
405 if (video->x > GB_VIDEO_HORIZONTAL_PIXELS) {
406 video->x = GB_VIDEO_HORIZONTAL_PIXELS;
407 } else if (video->x < 0) {
408 mLOG(GB, FATAL, "Video dot clock went negative!");
409 video->x = oldX;
410 }
411 if (video->frameskipCounter <= 0) {
412 video->renderer->drawRange(video->renderer, oldX, video->x, video->ly, video->objThisLine, video->objMax);
413 }
414}
415
416void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
417 if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && GBRegisterLCDCIsEnable(value)) {
418 video->mode = 2;
419 video->modeEvent.callback = _endMode2;
420 int32_t next = GB_VIDEO_MODE_2_LENGTH - 5; // TODO: Why is this fudge factor needed? Might be related to T-cycles for load/store differing
421 mTimingSchedule(&video->p->timing, &video->modeEvent, next << video->p->doubleSpeed);
422
423 video->ly = 0;
424 video->p->memory.io[REG_LY] = 0;
425 GBRegisterSTAT oldStat = video->stat;
426 video->stat = GBRegisterSTATSetMode(video->stat, 0);
427 video->stat = GBRegisterSTATSetLYC(video->stat, video->ly == video->p->memory.io[REG_LYC]);
428 if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
429 video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
430 GBUpdateIRQs(video->p);
431 }
432 video->p->memory.io[REG_STAT] = video->stat;
433 video->renderer->writePalette(video->renderer, 0, video->palette[0]);
434
435 mTimingDeschedule(&video->p->timing, &video->frameEvent);
436 }
437 if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
438 // TODO: Fix serialization; this gets internal and visible modes out of sync
439 video->stat = GBRegisterSTATSetMode(video->stat, 0);
440 video->p->memory.io[REG_STAT] = video->stat;
441 video->ly = 0;
442 video->p->memory.io[REG_LY] = 0;
443 video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]);
444
445 mTimingDeschedule(&video->p->timing, &video->modeEvent);
446 mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
447 }
448 video->p->memory.io[REG_STAT] = video->stat;
449}
450
451void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
452 GBRegisterSTAT oldStat = video->stat;
453 video->stat = (video->stat & 0x7) | (value & 0x78);
454 if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) || video->p->model >= GB_MODEL_CGB) {
455 return;
456 }
457 if (!_statIRQAsserted(video, oldStat) && video->mode < 3) {
458 // TODO: variable for the IRQ line value?
459 video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
460 GBUpdateIRQs(video->p);
461 }
462}
463
464void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
465 GBRegisterSTAT oldStat = video->stat;
466 video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly);
467 if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
468 video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
469 GBUpdateIRQs(video->p);
470 }
471 video->p->memory.io[REG_STAT] = video->stat;
472}
473
474void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value) {
475 if (video->p->model < GB_MODEL_SGB) {
476 switch (address) {
477 case REG_BGP:
478 video->palette[0] = video->dmgPalette[value & 3];
479 video->palette[1] = video->dmgPalette[(value >> 2) & 3];
480 video->palette[2] = video->dmgPalette[(value >> 4) & 3];
481 video->palette[3] = video->dmgPalette[(value >> 6) & 3];
482 video->renderer->writePalette(video->renderer, 0, video->palette[0]);
483 video->renderer->writePalette(video->renderer, 1, video->palette[1]);
484 video->renderer->writePalette(video->renderer, 2, video->palette[2]);
485 video->renderer->writePalette(video->renderer, 3, video->palette[3]);
486 break;
487 case REG_OBP0:
488 video->palette[8 * 4 + 0] = video->dmgPalette[(value & 3) + 4];
489 video->palette[8 * 4 + 1] = video->dmgPalette[((value >> 2) & 3) + 4];
490 video->palette[8 * 4 + 2] = video->dmgPalette[((value >> 4) & 3) + 4];
491 video->palette[8 * 4 + 3] = video->dmgPalette[((value >> 6) & 3) + 4];
492 video->renderer->writePalette(video->renderer, 8 * 4 + 0, video->palette[8 * 4 + 0]);
493 video->renderer->writePalette(video->renderer, 8 * 4 + 1, video->palette[8 * 4 + 1]);
494 video->renderer->writePalette(video->renderer, 8 * 4 + 2, video->palette[8 * 4 + 2]);
495 video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]);
496 break;
497 case REG_OBP1:
498 video->palette[9 * 4 + 0] = video->dmgPalette[(value & 3) + 8];
499 video->palette[9 * 4 + 1] = video->dmgPalette[((value >> 2) & 3) + 8];
500 video->palette[9 * 4 + 2] = video->dmgPalette[((value >> 4) & 3) + 8];
501 video->palette[9 * 4 + 3] = video->dmgPalette[((value >> 6) & 3) + 8];
502 video->renderer->writePalette(video->renderer, 9 * 4 + 0, video->palette[9 * 4 + 0]);
503 video->renderer->writePalette(video->renderer, 9 * 4 + 1, video->palette[9 * 4 + 1]);
504 video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]);
505 video->renderer->writePalette(video->renderer, 9 * 4 + 3, video->palette[9 * 4 + 3]);
506 break;
507 }
508 } else if (video->p->model == GB_MODEL_SGB) {
509 video->renderer->writeVideoRegister(video->renderer, address, value);
510 } else {
511 switch (address) {
512 case REG_BCPD:
513 if (video->bcpIndex & 1) {
514 video->palette[video->bcpIndex >> 1] &= 0x00FF;
515 video->palette[video->bcpIndex >> 1] |= value << 8;
516 } else {
517 video->palette[video->bcpIndex >> 1] &= 0xFF00;
518 video->palette[video->bcpIndex >> 1] |= value;
519 }
520 video->renderer->writePalette(video->renderer, video->bcpIndex >> 1, video->palette[video->bcpIndex >> 1]);
521 if (video->bcpIncrement) {
522 ++video->bcpIndex;
523 video->bcpIndex &= 0x3F;
524 video->p->memory.io[REG_BCPS] &= 0x80;
525 video->p->memory.io[REG_BCPS] |= video->bcpIndex;
526 }
527 video->p->memory.io[REG_BCPD] = video->palette[video->bcpIndex >> 1] >> (8 * (video->bcpIndex & 1));
528 break;
529 case REG_OCPD:
530 if (video->ocpIndex & 1) {
531 video->palette[8 * 4 + (video->ocpIndex >> 1)] &= 0x00FF;
532 video->palette[8 * 4 + (video->ocpIndex >> 1)] |= value << 8;
533 } else {
534 video->palette[8 * 4 + (video->ocpIndex >> 1)] &= 0xFF00;
535 video->palette[8 * 4 + (video->ocpIndex >> 1)] |= value;
536 }
537 video->renderer->writePalette(video->renderer, 8 * 4 + (video->ocpIndex >> 1), video->palette[8 * 4 + (video->ocpIndex >> 1)]);
538 if (video->ocpIncrement) {
539 ++video->ocpIndex;
540 video->ocpIndex &= 0x3F;
541 video->p->memory.io[REG_OCPS] &= 0x80;
542 video->p->memory.io[REG_OCPS] |= video->ocpIndex;
543 }
544 video->p->memory.io[REG_OCPD] = video->palette[8 * 4 + (video->ocpIndex >> 1)] >> (8 * (video->ocpIndex & 1));
545 break;
546 }
547 }
548}
549
550void GBVideoSwitchBank(struct GBVideo* video, uint8_t value) {
551 value &= 1;
552 video->vramBank = &video->vram[value * GB_SIZE_VRAM_BANK0];
553 video->vramCurrentBank = value;
554}
555
556void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color) {
557 if (index >= 12) {
558 return;
559 }
560 video->dmgPalette[index] = M_RGB8_TO_RGB5(color);
561}
562
563void GBVideoDisableCGB(struct GBVideo* video) {
564 video->dmgPalette[0] = video->palette[0];
565 video->dmgPalette[1] = video->palette[1];
566 video->dmgPalette[2] = video->palette[2];
567 video->dmgPalette[3] = video->palette[3];
568 video->dmgPalette[4] = video->palette[8 * 4 + 0];
569 video->dmgPalette[5] = video->palette[8 * 4 + 1];
570 video->dmgPalette[6] = video->palette[8 * 4 + 2];
571 video->dmgPalette[7] = video->palette[8 * 4 + 3];
572 video->dmgPalette[8] = video->palette[9 * 4 + 0];
573 video->dmgPalette[9] = video->palette[9 * 4 + 1];
574 video->dmgPalette[10] = video->palette[9 * 4 + 2];
575 video->dmgPalette[11] = video->palette[9 * 4 + 3];
576 video->renderer->deinit(video->renderer);
577 video->renderer->init(video->renderer, video->p->model, video->sgbBorders);
578}
579
580void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) {
581 int i;
582 if (!(video->sgbCommandHeader & 7)) {
583 if ((data[0] >> 3) > SGB_OBJ_TRN) {
584 video->sgbCommandHeader = 0;
585 return;
586 }
587 video->sgbCommandHeader = data[0];
588 }
589 --video->sgbCommandHeader;
590 switch (video->sgbCommandHeader >> 3) {
591 case SGB_PAL01:
592 video->palette[0] = data[1] | (data[2] << 8);
593 video->palette[1] = data[3] | (data[4] << 8);
594 video->palette[2] = data[5] | (data[6] << 8);
595 video->palette[3] = data[7] | (data[8] << 8);
596
597 video->palette[4] = data[1] | (data[2] << 8);
598 video->palette[5] = data[9] | (data[10] << 8);
599 video->palette[6] = data[11] | (data[12] << 8);
600 video->palette[7] = data[13] | (data[14] << 8);
601
602 video->palette[8] = data[1] | (data[2] << 8);
603 video->palette[12] = data[1] | (data[2] << 8);
604
605 video->renderer->writePalette(video->renderer, 0, video->palette[0]);
606 video->renderer->writePalette(video->renderer, 1, video->palette[1]);
607 video->renderer->writePalette(video->renderer, 2, video->palette[2]);
608 video->renderer->writePalette(video->renderer, 3, video->palette[3]);
609 video->renderer->writePalette(video->renderer, 4, video->palette[4]);
610 video->renderer->writePalette(video->renderer, 5, video->palette[5]);
611 video->renderer->writePalette(video->renderer, 6, video->palette[6]);
612 video->renderer->writePalette(video->renderer, 7, video->palette[7]);
613 video->renderer->writePalette(video->renderer, 8, video->palette[8]);
614 video->renderer->writePalette(video->renderer, 12, video->palette[12]);
615 break;
616 case SGB_PAL23:
617 video->palette[9] = data[3] | (data[4] << 8);
618 video->palette[10] = data[5] | (data[6] << 8);
619 video->palette[11] = data[7] | (data[8] << 8);
620
621 video->palette[13] = data[9] | (data[10] << 8);
622 video->palette[14] = data[11] | (data[12] << 8);
623 video->palette[15] = data[13] | (data[14] << 8);
624 video->renderer->writePalette(video->renderer, 9, video->palette[9]);
625 video->renderer->writePalette(video->renderer, 10, video->palette[10]);
626 video->renderer->writePalette(video->renderer, 11, video->palette[11]);
627 video->renderer->writePalette(video->renderer, 13, video->palette[13]);
628 video->renderer->writePalette(video->renderer, 14, video->palette[14]);
629 video->renderer->writePalette(video->renderer, 15, video->palette[15]);
630 break;
631 case SGB_PAL03:
632 video->palette[0] = data[1] | (data[2] << 8);
633 video->palette[1] = data[3] | (data[4] << 8);
634 video->palette[2] = data[5] | (data[6] << 8);
635 video->palette[3] = data[7] | (data[8] << 8);
636
637 video->palette[4] = data[1] | (data[2] << 8);
638 video->palette[8] = data[1] | (data[2] << 8);
639
640 video->palette[12] = data[1] | (data[2] << 8);
641 video->palette[13] = data[9] | (data[10] << 8);
642 video->palette[14] = data[11] | (data[12] << 8);
643 video->palette[15] = data[13] | (data[14] << 8);
644 video->renderer->writePalette(video->renderer, 0, video->palette[0]);
645 video->renderer->writePalette(video->renderer, 1, video->palette[1]);
646 video->renderer->writePalette(video->renderer, 2, video->palette[2]);
647 video->renderer->writePalette(video->renderer, 3, video->palette[3]);
648 video->renderer->writePalette(video->renderer, 4, video->palette[4]);
649 video->renderer->writePalette(video->renderer, 8, video->palette[8]);
650 video->renderer->writePalette(video->renderer, 12, video->palette[12]);
651 video->renderer->writePalette(video->renderer, 13, video->palette[13]);
652 video->renderer->writePalette(video->renderer, 14, video->palette[14]);
653 video->renderer->writePalette(video->renderer, 15, video->palette[15]);
654 break;
655 case SGB_PAL12:
656 video->palette[5] = data[3] | (data[4] << 8);
657 video->palette[6] = data[5] | (data[6] << 8);
658 video->palette[7] = data[7] | (data[8] << 8);
659
660 video->palette[9] = data[9] | (data[10] << 8);
661 video->palette[10] = data[11] | (data[12] << 8);
662 video->palette[11] = data[13] | (data[14] << 8);
663 video->renderer->writePalette(video->renderer, 5, video->palette[5]);
664 video->renderer->writePalette(video->renderer, 6, video->palette[6]);
665 video->renderer->writePalette(video->renderer, 7, video->palette[7]);
666 video->renderer->writePalette(video->renderer, 9, video->palette[9]);
667 video->renderer->writePalette(video->renderer, 10, video->palette[10]);
668 video->renderer->writePalette(video->renderer, 11, video->palette[11]);
669 break;
670 case SGB_PAL_SET:
671 for (i = 0; i < 4; ++i) {
672 uint16_t entry = (data[2 + (i * 2)] << 8) | data[1 + (i * 2)];
673 if (entry >= 0x200) {
674 mLOG(GB, STUB, "Unimplemented SGB palette overflow: %03X", entry);
675 continue;
676 }
677 LOAD_16LE(video->palette[i * 4 + 0], entry * 8 + 0, video->renderer->sgbPalRam);
678 video->renderer->writePalette(video->renderer, i * 4 + 0, video->palette[i * 4 + 0]);
679 LOAD_16LE(video->palette[i * 4 + 1], entry * 8 + 2, video->renderer->sgbPalRam);
680 video->renderer->writePalette(video->renderer, i * 4 + 1, video->palette[i * 4 + 1]);
681 LOAD_16LE(video->palette[i * 4 + 2], entry * 8 + 4, video->renderer->sgbPalRam);
682 video->renderer->writePalette(video->renderer, i * 4 + 2, video->palette[i * 4 + 2]);
683 LOAD_16LE(video->palette[i * 4 + 3], entry * 8 + 6, video->renderer->sgbPalRam);
684 video->renderer->writePalette(video->renderer, i * 4 + 3, video->palette[i * 4 + 3]);
685 }
686 break;
687 case SGB_ATTR_BLK:
688 case SGB_ATTR_CHR:
689 case SGB_PAL_TRN:
690 case SGB_ATRC_EN:
691 case SGB_CHR_TRN:
692 case SGB_PCT_TRN:
693 case SGB_ATTR_TRN:
694 case SGB_ATTR_SET:
695 break;
696 case SGB_MLT_REG:
697 return;
698 case SGB_MASK_EN:
699 video->renderer->sgbRenderMode = data[1] & 0x3;
700 break;
701 default:
702 mLOG(GB, STUB, "Unimplemented SGB command: %02X", data[0] >> 3);
703 return;
704 }
705 video->renderer->writeSGBPacket(video->renderer, data);
706}
707
708static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders) {
709 UNUSED(renderer);
710 UNUSED(model);
711 UNUSED(borders);
712 // Nothing to do
713}
714
715static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer) {
716 UNUSED(renderer);
717 // Nothing to do
718}
719
720static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
721 if (renderer->cache) {
722 GBVideoCacheWriteVideoRegister(renderer->cache, address, value);
723 }
724 return value;
725}
726
727static void GBVideoDummyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
728 UNUSED(renderer);
729 UNUSED(data);
730}
731
732static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
733 if (renderer->cache) {
734 mCacheSetWriteVRAM(renderer->cache, address);
735 }
736}
737
738static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
739 UNUSED(renderer);
740 UNUSED(oam);
741 // Nothing to do
742}
743
744static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) {
745 if (renderer->cache) {
746 mCacheSetWritePalette(renderer->cache, index, mColorFrom555(value));
747 }
748}
749
750static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) {
751 UNUSED(renderer);
752 UNUSED(endX);
753 UNUSED(startX);
754 UNUSED(y);
755 UNUSED(obj);
756 UNUSED(oamMax);
757 // Nothing to do
758}
759
760static void GBVideoDummyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
761 UNUSED(renderer);
762 UNUSED(y);
763 // Nothing to do
764}
765
766static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer) {
767 UNUSED(renderer);
768 // Nothing to do
769}
770
771static void GBVideoDummyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {
772 UNUSED(renderer);
773 UNUSED(stride);
774 UNUSED(pixels);
775 // Nothing to do
776}
777
778static void GBVideoDummyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) {
779 UNUSED(renderer);
780 UNUSED(stride);
781 UNUSED(pixels);
782 // Nothing to do
783}
784
785void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state) {
786 STORE_16LE(video->x, 0, &state->video.x);
787 STORE_16LE(video->ly, 0, &state->video.ly);
788 STORE_32LE(video->frameCounter, 0, &state->video.frameCounter);
789 STORE_32LE(video->dotClock, 0, &state->video.dotCounter);
790 state->video.vramCurrentBank = video->vramCurrentBank;
791
792 GBSerializedVideoFlags flags = 0;
793 flags = GBSerializedVideoFlagsSetBcpIncrement(flags, video->bcpIncrement);
794 flags = GBSerializedVideoFlagsSetOcpIncrement(flags, video->ocpIncrement);
795 flags = GBSerializedVideoFlagsSetMode(flags, video->mode);
796 flags = GBSerializedVideoFlagsSetNotModeEventScheduled(flags, !mTimingIsScheduled(&video->p->timing, &video->modeEvent));
797 flags = GBSerializedVideoFlagsSetNotFrameEventScheduled(flags, !mTimingIsScheduled(&video->p->timing, &video->frameEvent));
798 state->video.flags = flags;
799 STORE_16LE(video->bcpIndex, 0, &state->video.bcpIndex);
800 STORE_16LE(video->ocpIndex, 0, &state->video.ocpIndex);
801
802 size_t i;
803 for (i = 0; i < 64; ++i) {
804 STORE_16LE(video->palette[i], i * 2, state->video.palette);
805 }
806
807 STORE_32LE(video->modeEvent.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextMode);
808 STORE_32LE(video->frameEvent.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextFrame);
809
810 memcpy(state->vram, video->vram, GB_SIZE_VRAM);
811 memcpy(state->oam, &video->oam.raw, GB_SIZE_OAM);
812}
813
814void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state) {
815 LOAD_16LE(video->x, 0, &state->video.x);
816 LOAD_16LE(video->ly, 0, &state->video.ly);
817 LOAD_32LE(video->frameCounter, 0, &state->video.frameCounter);
818 LOAD_32LE(video->dotClock, 0, &state->video.dotCounter);
819 video->vramCurrentBank = state->video.vramCurrentBank;
820
821 GBSerializedVideoFlags flags = state->video.flags;
822 video->bcpIncrement = GBSerializedVideoFlagsGetBcpIncrement(flags);
823 video->ocpIncrement = GBSerializedVideoFlagsGetOcpIncrement(flags);
824 video->mode = GBSerializedVideoFlagsGetMode(flags);
825 LOAD_16LE(video->bcpIndex, 0, &state->video.bcpIndex);
826 video->bcpIndex &= 0x3F;
827 LOAD_16LE(video->ocpIndex, 0, &state->video.ocpIndex);
828 video->ocpIndex &= 0x3F;
829
830 switch (video->mode) {
831 case 0:
832 video->modeEvent.callback = _endMode0;
833 break;
834 case 1:
835 video->modeEvent.callback = _endMode1;
836 break;
837 case 2:
838 video->modeEvent.callback = _endMode2;
839 break;
840 case 3:
841 video->modeEvent.callback = _endMode3;
842 break;
843 }
844
845 uint32_t when;
846 if (!GBSerializedVideoFlagsIsNotModeEventScheduled(flags)) {
847 LOAD_32LE(when, 0, &state->video.nextMode);
848 mTimingSchedule(&video->p->timing, &video->modeEvent, when);
849 }
850 if (!GBSerializedVideoFlagsIsNotFrameEventScheduled(flags)) {
851 LOAD_32LE(when, 0, &state->video.nextFrame);
852 mTimingSchedule(&video->p->timing, &video->frameEvent, when);
853 }
854
855 size_t i;
856 for (i = 0; i < 64; ++i) {
857 LOAD_16LE(video->palette[i], i * 2, state->video.palette);
858 video->renderer->writePalette(video->renderer, i, video->palette[i]);
859 }
860
861 memcpy(video->vram, state->vram, GB_SIZE_VRAM);
862 memcpy(&video->oam.raw, state->oam, GB_SIZE_OAM);
863
864 _cleanOAM(video, video->ly);
865 GBVideoSwitchBank(video, video->vramCurrentBank);
866
867 video->renderer->deinit(video->renderer);
868 video->renderer->init(video->renderer, video->p->model, video->sgbBorders);
869}