1 /***************************************************************************
2  *   Copyright (C) 2007 by Sindre Aamås                                    *
3  *   aamas@stud.ntnu.no                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License version 2 as     *
7  *   published by the Free Software Foundation.                            *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License version 2 for more details.                *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   version 2 along with this program; if not, write to the               *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 #ifndef VIDEO_H
20 #define VIDEO_H
21 
22 #include "interruptrequester.h"
23 #include "video/lyc_irq.h"
24 #include "video/m0_irq.h"
25 #include "video/next_m0_time.h"
26 #include "video/ppu.h"
27 #include <memory>
28 
29 namespace gambatte {
30 
31 class VideoInterruptRequester
32 {
33    public:
VideoInterruptRequester(InterruptRequester & intreq)34       explicit VideoInterruptRequester(InterruptRequester &intreq) : intreq_(intreq) {}
flagHdmaReq()35       void flagHdmaReq() const { gambatte::flagHdmaReq(intreq_); }
flagIrq(const unsigned bit)36       void flagIrq(const unsigned bit) const { intreq_.flagIrq(bit); }
setNextEventTime(const unsigned long time)37       void setNextEventTime(const unsigned long time) const { intreq_.setEventTime<intevent_video>(time); }
38 
39    private:
40       InterruptRequester &intreq_;
41 };
42 
43 class LCD
44 {
45    public:
46       LCD(const unsigned char *oamram, const unsigned char *vram_in, VideoInterruptRequester memEventRequester);
47       void reset(const unsigned char *oamram, unsigned char const *vram, bool cgb);
48       void setStatePtrs(SaveState &state);
49       void saveState(SaveState &state) const;
50       void loadState(const SaveState &state, const unsigned char *oamram);
51       void setDmgPaletteColor(unsigned palNum, unsigned colorNum, video_pixel_t rgb32);
52       void setVideoBuffer(video_pixel_t *videoBuf, int pitch);
setDmgMode(bool mode)53       void setDmgMode(bool mode) { ppu_.setDmgMode(mode); }
54 
swapToDMG()55       void swapToDMG() {
56          ppu_.setDmgMode(true);
57          refreshPalettes();
58       }
59 
dmgBgPaletteChange(const unsigned data,const unsigned long cycleCounter)60       void dmgBgPaletteChange(const unsigned data, const unsigned long cycleCounter) {
61          update(cycleCounter);
62          bgpData_[0] = data;
63          setDmgPalette(ppu_.bgPalette(), dmgColorsRgb32_, data);
64       }
65 
dmgSpPalette1Change(const unsigned data,const unsigned long cycleCounter)66       void dmgSpPalette1Change(const unsigned data, const unsigned long cycleCounter) {
67          update(cycleCounter);
68          objpData_[0] = data;
69          setDmgPalette(ppu_.spPalette(), dmgColorsRgb32_ + 4, data);
70       }
71 
dmgSpPalette2Change(const unsigned data,const unsigned long cycleCounter)72       void dmgSpPalette2Change(const unsigned data, const unsigned long cycleCounter) {
73          update(cycleCounter);
74          objpData_[1] = data;
75          setDmgPalette(ppu_.spPalette() + 4, dmgColorsRgb32_ + 8, data);
76       }
77 
cgbBgColorChange(unsigned index,const unsigned data,const unsigned long cycleCounter)78       void cgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
79          if (bgpData_[index] != data) {
80             doCgbBgColorChange(index, data, cycleCounter);
81             if(index < 8)
82                doCgbColorChange(dmgColorsGBC_, dmgColorsRgb32_, index, data);
83          }
84       }
85 
cgbSpColorChange(unsigned index,const unsigned data,const unsigned long cycleCounter)86       void cgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
87          if (objpData_[index] != data) {
88             doCgbSpColorChange(index, data, cycleCounter);
89             if(index < 8 * 2/*dmg has 2 sprite banks*/)
90                doCgbColorChange(dmgColorsGBC_ + 8, dmgColorsRgb32_ + 4, index, data);
91          }
92       }
93 
cgbBgColorRead(const unsigned index,const unsigned long cycleCounter)94       unsigned cgbBgColorRead(const unsigned index, const unsigned long cycleCounter) {
95          return (ppu_.cgb() & cgbpAccessible(cycleCounter)) ? bgpData_[index] : 0xFF;
96       }
97 
cgbSpColorRead(const unsigned index,const unsigned long cycleCounter)98       unsigned cgbSpColorRead(const unsigned index, const unsigned long cycleCounter) {
99          return (ppu_.cgb() & cgbpAccessible(cycleCounter)) ? objpData_[index] : 0xFF;
100       }
101 
102       void updateScreen(bool blanklcd, unsigned long cc);
103       void resetCc(unsigned long oldCC, unsigned long newCc);
104       void speedChange(unsigned long cycleCounter);
105       bool vramAccessible(unsigned long cycleCounter);
106       bool oamReadable(unsigned long cycleCounter);
107       bool oamWritable(unsigned long cycleCounter);
108       void wxChange(unsigned newValue, unsigned long cycleCounter);
109       void wyChange(unsigned newValue, unsigned long cycleCounter);
110       void oamChange(unsigned long cycleCounter);
111       void oamChange(const unsigned char *oamram, unsigned long cycleCounter);
112       void scxChange(unsigned newScx, unsigned long cycleCounter);
113       void scyChange(unsigned newValue, unsigned long cycleCounter);
114 
vramChange(const unsigned long cycleCounter)115       void vramChange(const unsigned long cycleCounter) { update(cycleCounter); }
116 
117       unsigned getStat(unsigned lycReg, unsigned long cycleCounter);
118 
getLyReg(const unsigned long cycleCounter)119       unsigned getLyReg(const unsigned long cycleCounter) {
120          unsigned lyReg = 0;
121 
122          if (ppu_.lcdc() & 0x80) {
123             if (cycleCounter >= ppu_.lyCounter().time())
124                update(cycleCounter);
125 
126             lyReg = ppu_.lyCounter().ly();
127 
128             if (lyReg == 153) {
129                if (isDoubleSpeed()) {
130                   if (ppu_.lyCounter().time() - cycleCounter <= 456 * 2 - 8)
131                      lyReg = 0;
132                } else
133                   lyReg = 0;
134             } else if (ppu_.lyCounter().time() - cycleCounter <= 4)
135                ++lyReg;
136          }
137 
138          return lyReg;
139       }
140 
nextMode1IrqTime()141       unsigned long nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
142 
143       void lcdcChange(unsigned data, unsigned long cycleCounter);
144       void lcdstatChange(unsigned data, unsigned long cycleCounter);
145       void lycRegChange(unsigned data, unsigned long cycleCounter);
146 
147       void enableHdma(unsigned long cycleCounter);
148       void disableHdma(unsigned long cycleCounter);
hdmaIsEnabled()149       bool hdmaIsEnabled() const { return eventTimes_(HDMA_REQ) != disabled_time; }
150 
151       void update(unsigned long cycleCounter);
152 
isCgb()153       bool isCgb() const { return ppu_.cgb(); }
isDoubleSpeed()154       bool isDoubleSpeed() const { return ppu_.lyCounter().isDoubleSpeed(); }
155 
156       void setColorCorrection(bool colorCorrection);
157       void setColorCorrectionMode(unsigned colorCorrectionMode);
158       void setColorCorrectionBrightness(float colorCorrectionBrightness);
159       void setDarkFilterLevel(unsigned darkFilterLevel);
160       video_pixel_t gbcToRgb32(const unsigned bgr15);
161    private:
162       enum Event { MEM_EVENT, LY_COUNT }; enum { NUM_EVENTS = LY_COUNT + 1 };
163       enum MemEvent { ONESHOT_LCDSTATIRQ, ONESHOT_UPDATEWY2, MODE1_IRQ, LYC_IRQ, SPRITE_MAP,
164          HDMA_REQ, MODE2_IRQ, MODE0_IRQ }; enum { NUM_MEM_EVENTS = MODE0_IRQ + 1 };
165 
166       class EventTimes
167       {
168          public:
EventTimes(const VideoInterruptRequester memEventRequester)169             explicit EventTimes(const VideoInterruptRequester memEventRequester) : memEventRequester_(memEventRequester) {}
170 
nextEvent()171             Event nextEvent() const { return static_cast<Event>(eventMin_.min()); }
nextEventTime()172             unsigned long nextEventTime() const { return eventMin_.minValue(); }
operator()173             unsigned long operator()(const Event e) const { return eventMin_.value(e); }
set(const unsigned long time)174             template<Event e> void set(const unsigned long time) { eventMin_.setValue<e>(time); }
set(const Event e,const unsigned long time)175             void set(const Event e, const unsigned long time) { eventMin_.setValue(e, time); }
176 
nextMemEvent()177             MemEvent nextMemEvent() const { return static_cast<MemEvent>(memEventMin_.min()); }
nextMemEventTime()178             unsigned long nextMemEventTime() const { return memEventMin_.minValue(); }
operator()179             unsigned long operator()(const MemEvent e) const { return memEventMin_.value(e); }
setm(const unsigned long time)180             template<MemEvent e> void setm(const unsigned long time) { memEventMin_.setValue<e>(time); setMemEvent(); }
set(const MemEvent e,const unsigned long time)181             void set(const MemEvent e, const unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); }
182 
flagIrq(const unsigned bit)183             void flagIrq(const unsigned bit) { memEventRequester_.flagIrq(bit); }
flagHdmaReq()184             void flagHdmaReq() { memEventRequester_.flagHdmaReq(); }
185 
186          private:
187             MinKeeper<NUM_EVENTS> eventMin_;
188             MinKeeper<NUM_MEM_EVENTS> memEventMin_;
189             VideoInterruptRequester memEventRequester_;
190 
setMemEvent()191             void setMemEvent() {
192                const unsigned long nmet = nextMemEventTime();
193                eventMin_.setValue<MEM_EVENT>(nmet);
194                memEventRequester_.setNextEventTime(nmet);
195             }
196 
197       };
198 
199       PPU ppu_;
200       video_pixel_t dmgColorsRgb32_[3 * 4];
201       unsigned char dmgColorsGBC_[3 * 8];
202       unsigned char  bgpData_[8 * 8];
203       unsigned char objpData_[8 * 8];
204 
205       EventTimes eventTimes_;
206       M0Irq m0Irq_;
207       LycIrq lycIrq_;
208       NextM0Time nextM0Time_;
209 
210       unsigned char statReg_;
211       unsigned char m2IrqStatReg_;
212       unsigned char m1IrqStatReg_;
213 
214       static void setDmgPalette(video_pixel_t *palette, const video_pixel_t *dmgColors, unsigned data);
215       void setDmgPaletteColor(unsigned index, video_pixel_t rgb32);
216 
217       void setDBuffer();
218       void refreshPalettes();
219 
220       void doMode2IrqEvent();
221       void event();
222 
223       unsigned long m0TimeOfCurrentLine(unsigned long cc);
224       bool cgbpAccessible(unsigned long cycleCounter);
225 
226       void mode3CyclesChange();
227       void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
228       void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
229 
230       bool colorCorrection;
231       unsigned colorCorrectionMode;
232       float colorCorrectionBrightness;
233       unsigned darkFilterLevel;
234       void doCgbColorChange(unsigned char *const pdata,
235             video_pixel_t *const palette, unsigned index, const unsigned data);
236 
237       void darkenRgb(float &r, float &g, float &b);
238 
239 };
240 
241 }
242 
243 #endif
244