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 #include "sound.h"
20 #include "savestate.h"
21 #include <cstring>
22 #include <algorithm>
23 
24 /*
25 	Frame Sequencer
26 
27 	Step    Length Ctr  Vol Env     Sweep
28 	- - - - - - - - - - - - - - - - - - - -
29 	0       Clock       -           Clock
30 S	1       -           Clock       -
31 	2       Clock       -           -
32 	3       -           -           -
33 	4       Clock       -           Clock
34 	5       -           -           -
35 	6       Clock       -           -
36 	7       -           -           -
37 	- - - - - - - - - - - - - - - - - - - -
38 	Rate    256 Hz      64 Hz       128 Hz
39 
40 S) start step on sound power on.
41 */
42 
43 namespace gambatte
44 {
45 
PSG()46    PSG::PSG()
47       :  buffer_(0)
48       ,  bufferPos_(0)
49       ,  lastUpdate_(0)
50       ,  soVol_(0)
51       ,  rsum_(0x8000) // initialize to 0x8000 to prevent borrows from high word, xor away later
52       ,  enabled_(false)
53    {
54    }
55 
init(const bool cgb)56    void PSG::init(const bool cgb)
57    {
58       ch1_.init(cgb);
59       ch3_.init(cgb);
60    }
61 
reset()62    void PSG::reset()
63    {
64       ch1_.reset();
65       ch2_.reset();
66       ch3_.reset();
67       ch4_.reset();
68    }
69 
setStatePtrs(SaveState & state)70    void PSG::setStatePtrs(SaveState &state)
71    {
72       ch3_.setStatePtrs(state);
73    }
74 
saveState(SaveState & state)75    void PSG::saveState(SaveState &state)
76    {
77       ch1_.saveState(state);
78       ch2_.saveState(state);
79       ch3_.saveState(state);
80       ch4_.saveState(state);
81    }
82 
loadState(const SaveState & state)83    void PSG::loadState(const SaveState &state)
84    {
85       ch1_.loadState(state);
86       ch2_.loadState(state);
87       ch3_.loadState(state);
88       ch4_.loadState(state);
89 
90       lastUpdate_ = state.cpu.cycleCounter;
91       setSoVolume(state.mem.ioamhram.get()[0x124]);
92       mapSo(state.mem.ioamhram.get()[0x125]);
93       enabled_ = state.mem.ioamhram.get()[0x126] >> 7 & 1;
94    }
95 
accumulateChannels(const unsigned long cycles)96    void PSG::accumulateChannels(const unsigned long cycles)
97    {
98       uint_least32_t *const buf = buffer_ + bufferPos_;
99 
100       std::memset(buf, 0, cycles * sizeof(uint_least32_t));
101       ch1_.update(buf, soVol_, cycles);
102       ch2_.update(buf, soVol_, cycles);
103       ch3_.update(buf, soVol_, cycles);
104       ch4_.update(buf, soVol_, cycles);
105    }
106 
generateSamples(unsigned long const cycleCounter,bool const doubleSpeed)107    void PSG::generateSamples(unsigned long const cycleCounter, bool const doubleSpeed)
108    {
109       unsigned long const cycles = (cycleCounter - lastUpdate_) >> (1 + doubleSpeed);
110       lastUpdate_ += cycles << (1 + doubleSpeed);
111 
112       if (cycles)
113          accumulateChannels(cycles);
114 
115       bufferPos_ += cycles;
116    }
117 
resetCounter(unsigned long newCc,unsigned long oldCc,bool doubleSpeed)118    void PSG::resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed)
119    {
120       generateSamples(oldCc, doubleSpeed);
121       lastUpdate_ = newCc - (oldCc - lastUpdate_);
122    }
123 
fillBuffer()124    size_t PSG::fillBuffer()
125    {
126       uint_least32_t sum = rsum_;
127       uint_least32_t *b = buffer_;
128       unsigned n = bufferPos_;
129 
130       if (unsigned n2 = n >> 3)
131       {
132          n -= n2 << 3;
133 
134          do
135          {
136             sum += b[0];
137             b[0] = sum ^ 0x8000;
138             sum += b[1];
139             b[1] = sum ^ 0x8000;
140             sum += b[2];
141             b[2] = sum ^ 0x8000;
142             sum += b[3];
143             b[3] = sum ^ 0x8000;
144             sum += b[4];
145             b[4] = sum ^ 0x8000;
146             sum += b[5];
147             b[5] = sum ^ 0x8000;
148             sum += b[6];
149             b[6] = sum ^ 0x8000;
150             sum += b[7];
151             b[7] = sum ^ 0x8000;
152 
153             b += 8;
154          } while (--n2);
155       }
156 
157       while (n--)
158       {
159          sum += *b;
160          /* xor away the initial rsum value of 0x8000 (which prevents
161           * borrows from the high word) from the low word */
162          *b++ = sum ^ 0x8000;
163       }
164 
165       rsum_ = sum;
166 
167       return bufferPos_;
168    }
169 
170 #ifdef WORDS_BIGENDIAN
171    static const unsigned long so1Mul = 0x00000001;
172    static const unsigned long so2Mul = 0x00010000;
173 #else
174    static const unsigned long so1Mul = 0x00010000;
175    static const unsigned long so2Mul = 0x00000001;
176 #endif
177 
setSoVolume(const unsigned nr50)178    void PSG::setSoVolume(const unsigned nr50)
179    {
180       soVol_ = (((nr50      & 0x7) + 1) * so1Mul
181             +  ((nr50 >> 4 & 0x7) + 1) * so2Mul) * 64;
182    }
183 
mapSo(const unsigned nr51)184    void PSG::mapSo(const unsigned nr51)
185    {
186       const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
187 
188       ch1_.setSo((tmp      & 0x00010001) * 0xFFFF);
189       ch2_.setSo((tmp >> 1 & 0x00010001) * 0xFFFF);
190       ch3_.setSo((tmp >> 2 & 0x00010001) * 0xFFFF);
191       ch4_.setSo((tmp >> 3 & 0x00010001) * 0xFFFF);
192    }
193 
getStatus() const194    unsigned PSG::getStatus() const
195    {
196       return ch1_.isActive()
197          | ch2_.isActive() << 1
198          | ch3_.isActive() << 2
199          | ch4_.isActive() << 3;
200    }
201 
202 }
203