src/ds/video.c (view raw)
1/* Copyright (c) 2013-2015 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/ds/video.h>
7
8#include <mgba/core/sync.h>
9#include <mgba/internal/ds/ds.h>
10#include <mgba/internal/gba/video.h>
11
12#include <mgba-util/memory.h>
13
14mLOG_DEFINE_CATEGORY(DS_VIDEO, "DS Video");
15
16static void _startHblank7(struct mTiming*, void* context, uint32_t cyclesLate);
17static void _startHdraw7(struct mTiming*, void* context, uint32_t cyclesLate);
18static void _startHblank9(struct mTiming*, void* context, uint32_t cyclesLate);
19static void _startHdraw9(struct mTiming*, void* context, uint32_t cyclesLate);
20
21void DSVideoInit(struct DSVideo* video) {
22 video->vram = NULL;
23 video->frameskip = 0;
24 video->event7.name = "DS7 Video";
25 video->event7.callback = NULL;
26 video->event7.context = video;
27 video->event7.priority = 8;
28 video->event9.name = "DS9 Video";
29 video->event9.callback = NULL;
30 video->event9.context = video;
31 video->event9.priority = 8;
32}
33
34void DSVideoReset(struct DSVideo* video) {
35 video->vcount = 0;
36 video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
37 video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
38
39 video->event7.callback = _startHblank7;
40 video->event9.callback = _startHblank9;
41 mTimingSchedule(&video->p->ds7.timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH);
42 mTimingSchedule(&video->p->ds9.timing, &video->event9, DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH);
43
44 video->frameCounter = 0;
45 video->frameskipCounter = 0;
46
47 if (video->vram) {
48 mappedMemoryFree(video->vram, DS_SIZE_VRAM);
49 }
50 video->vram = anonymousMemoryMap(DS_SIZE_VRAM);
51}
52
53void DSVideoDeinit(struct DSVideo* video) {
54 mappedMemoryFree(video->vram, DS_SIZE_VRAM);
55}
56
57void _startHdraw7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
58 struct DSVideo* video = context;
59 GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
60 dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
61 video->event7.callback = _startHblank7;
62 mTimingSchedule(timing, &video->event7, DS_VIDEO_HORIZONTAL_LENGTH - DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
63
64 ++video->vcount;
65 if (video->vcount == DS_VIDEO_VERTICAL_TOTAL_PIXELS) {
66 video->vcount = 0;
67 }
68 video->p->ds7.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
69
70 if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
71 dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
72 if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
73 DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VCOUNTER);
74 }
75 } else {
76 dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
77 }
78 video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
79
80 switch (video->vcount) {
81 case DS_VIDEO_VERTICAL_PIXELS:
82 video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
83 if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
84 DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_VBLANK);
85 }
86 break;
87 case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
88 video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
89 break;
90 }
91}
92
93void _startHblank7(struct mTiming* timing, void* context, uint32_t cyclesLate) {
94 struct DSVideo* video = context;
95 GBARegisterDISPSTAT dispstat = video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1];
96 dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
97 video->event7.callback = _startHdraw7;
98 mTimingSchedule(timing, &video->event7, DS7_VIDEO_HBLANK_LENGTH - cyclesLate);
99
100 // Begin Hblank
101 dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
102
103 if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
104 DSRaiseIRQ(video->p->ds7.cpu, video->p->ds7.memory.io, DS_IRQ_HBLANK);
105 }
106 video->p->ds7.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
107}
108
109void _startHdraw9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
110 struct DSVideo* video = context;
111 GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
112 dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
113 video->event9.callback = _startHblank9;
114 mTimingSchedule(timing, &video->event9, DS_VIDEO_HORIZONTAL_LENGTH - DS9_VIDEO_HBLANK_LENGTH - cyclesLate);
115
116 ++video->vcount;
117 if (video->vcount == DS_VIDEO_VERTICAL_TOTAL_PIXELS) {
118 video->vcount = 0;
119 }
120 video->p->ds9.memory.io[DS_REG_VCOUNT >> 1] = video->vcount;
121
122 if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
123 dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
124 if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
125 DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VCOUNTER);
126 }
127 } else {
128 dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
129 }
130 video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
131
132 // Note: state may be recorded during callbacks, so ensure it is consistent!
133 switch (video->vcount) {
134 case 0:
135 DSFrameStarted(video->p);
136 break;
137 case DS_VIDEO_VERTICAL_PIXELS:
138 video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);
139 if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
140 DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_VBLANK);
141 }
142 DSFrameEnded(video->p);
143 --video->frameskipCounter;
144 if (video->frameskipCounter < 0) {
145 mCoreSyncPostFrame(video->p->sync);
146 video->frameskipCounter = video->frameskip;
147 }
148 ++video->frameCounter;
149 break;
150 case DS_VIDEO_VERTICAL_TOTAL_PIXELS - 1:
151 video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = GBARegisterDISPSTATClearInVblank(dispstat);
152 break;
153 }
154}
155
156void _startHblank9(struct mTiming* timing, void* context, uint32_t cyclesLate) {
157 struct DSVideo* video = context;
158 GBARegisterDISPSTAT dispstat = video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1];
159 dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
160 video->event9.callback = _startHdraw9;
161 mTimingSchedule(timing, &video->event9, DS9_VIDEO_HBLANK_LENGTH - cyclesLate);
162
163 // Begin Hblank
164 dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
165
166 if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
167 DSRaiseIRQ(video->p->ds9.cpu, video->p->ds9.memory.io, DS_IRQ_HBLANK);
168 }
169 video->p->ds9.memory.io[DS_REG_DISPSTAT >> 1] = dispstat;
170}
171
172void DSVideoWriteDISPSTAT(struct DSCommon* dscore, uint16_t value) {
173 dscore->memory.io[DS_REG_DISPSTAT >> 1] &= 0x7;
174 dscore->memory.io[DS_REG_DISPSTAT >> 1] |= value;
175 // TODO: Does a VCounter IRQ trigger on write?
176}