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