/******************************************************************************/ /* Mednafen Virtual Boy Emulation Module */ /******************************************************************************/ /* vsu.cpp: ** Copyright (C) 2010-2016 Mednafen Team ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License ** as published by the Free Software Foundation; either version 2 ** of the License, or (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, Inc., ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vb.h" static const unsigned int Tap_LUT[8] = {15 - 1, 11 - 1, 14 - 1, 5 - 1, 9 - 1, 7 - 1, 10 - 1, 12 - 1}; VSU::VSU() { Synth.volume(1.0 / 6 / 2); for (int ch = 0; ch < 6; ch++) { for (int lr = 0; lr < 2; lr++) last_output[ch][lr] = 0; } } VSU::~VSU() { } void VSU::SetSoundRate(double rate) { for (int y = 0; y < 2; y++) { sbuf[y].set_sample_rate(rate ? rate : 44100, 50); sbuf[y].clock_rate((long)(VB_MASTER_CLOCK / 4)); sbuf[y].bass_freq(20); } } void VSU::Power(void) { SweepControl = 0; SweepModCounter = 0; SweepModClockDivider = 1; for (int ch = 0; ch < 6; ch++) { IntlControl[ch] = 0; LeftLevel[ch] = 0; RightLevel[ch] = 0; Frequency[ch] = 0; EnvControl[ch] = 0; RAMAddress[ch] = 0; EffFreq[ch] = 0; Envelope[ch] = 0; WavePos[ch] = 0; FreqCounter[ch] = 1; IntervalCounter[ch] = 0; EnvelopeCounter[ch] = 1; EffectsClockDivider[ch] = 4800; IntervalClockDivider[ch] = 4; EnvelopeClockDivider[ch] = 4; LatcherClockDivider[ch] = 120; } NoiseLatcherClockDivider = 120; NoiseLatcher = 0; memset(WaveData, 0, sizeof(WaveData)); memset(ModData, 0, sizeof(ModData)); last_ts = 0; } void VSU::Write(int32 timestamp, uint32 A, uint8 V) { A &= 0x7FF; Update(timestamp); //printf("VSU Write: %d, %08x %02x\n", timestamp, A, V); if (A < 0x280) WaveData[A >> 7][(A >> 2) & 0x1F] = V & 0x3F; else if (A < 0x400) { //if(A >= 0x300) // printf("Modulation mirror write? %08x %02x\n", A, V); ModData[(A >> 2) & 0x1F] = V; } else if (A < 0x600) { int ch = (A >> 6) & 0xF; //if(ch < 6) //printf("Ch: %d, Reg: %d, Value: %02x\n", ch, (A >> 2) & 0xF, V); if (ch > 5) { if (A == 0x580 && (V & 1)) { //puts("STOP, HAMMER TIME"); for (int i = 0; i < 6; i++) IntlControl[i] &= ~0x80; } } else switch ((A >> 2) & 0xF) { case 0x0: IntlControl[ch] = V & ~0x40; if (V & 0x80) { EffFreq[ch] = Frequency[ch]; if (ch == 5) FreqCounter[ch] = 10 * (2048 - EffFreq[ch]); else FreqCounter[ch] = 2048 - EffFreq[ch]; IntervalCounter[ch] = (V & 0x1F) + 1; EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1; if (ch == 4) { SweepModCounter = (SweepControl >> 4) & 7; SweepModClockDivider = (SweepControl & 0x80) ? 8 : 1; ModWavePos = 0; } WavePos[ch] = 0; if (ch == 5) // Not sure if this is correct. lfsr = 1; //if(!(IntlControl[ch] & 0x80)) // Envelope[ch] = (EnvControl[ch] >> 4) & 0xF; EffectsClockDivider[ch] = 4800; IntervalClockDivider[ch] = 4; EnvelopeClockDivider[ch] = 4; } break; case 0x1: LeftLevel[ch] = (V >> 4) & 0xF; RightLevel[ch] = (V >> 0) & 0xF; break; case 0x2: Frequency[ch] &= 0xFF00; Frequency[ch] |= V << 0; EffFreq[ch] &= 0xFF00; EffFreq[ch] |= V << 0; break; case 0x3: Frequency[ch] &= 0x00FF; Frequency[ch] |= (V & 0x7) << 8; EffFreq[ch] &= 0x00FF; EffFreq[ch] |= (V & 0x7) << 8; break; case 0x4: EnvControl[ch] &= 0xFF00; EnvControl[ch] |= V << 0; Envelope[ch] = (V >> 4) & 0xF; break; case 0x5: EnvControl[ch] &= 0x00FF; if (ch == 4) EnvControl[ch] |= (V & 0x73) << 8; else if (ch == 5) { EnvControl[ch] |= (V & 0x73) << 8; lfsr = 1; } else EnvControl[ch] |= (V & 0x03) << 8; break; case 0x6: RAMAddress[ch] = V & 0xF; break; case 0x7: if (ch == 4) { SweepControl = V; } break; } } } INLINE void VSU::CalcCurrentOutput(int ch, int &left, int &right) { if (!(IntlControl[ch] & 0x80)) { left = right = 0; return; } int WD; int l_ol, r_ol; if (ch == 5) WD = NoiseLatcher; //(NoiseLatcher << 6) - NoiseLatcher; else { if (RAMAddress[ch] > 4) WD = 0; else WD = WaveData[RAMAddress[ch]][WavePos[ch]]; // - 0x20; } l_ol = Envelope[ch] * LeftLevel[ch]; if (l_ol) { l_ol >>= 3; l_ol += 1; } r_ol = Envelope[ch] * RightLevel[ch]; if (r_ol) { r_ol >>= 3; r_ol += 1; } left = WD * l_ol; right = WD * r_ol; } void VSU::Update(int32 timestamp) { //puts("VSU Start"); int left, right; for (int ch = 0; ch < 6; ch++) { int32 clocks = timestamp - last_ts; int32 running_timestamp = last_ts; // Output sound here CalcCurrentOutput(ch, left, right); Synth.offset_inline(running_timestamp, left - last_output[ch][0], &sbuf[0]); Synth.offset_inline(running_timestamp, right - last_output[ch][1], &sbuf[1]); last_output[ch][0] = left; last_output[ch][1] = right; if (!(IntlControl[ch] & 0x80)) continue; while (clocks > 0) { int32 chunk_clocks = clocks; if (chunk_clocks > EffectsClockDivider[ch]) chunk_clocks = EffectsClockDivider[ch]; if (ch == 5) { if (chunk_clocks > NoiseLatcherClockDivider) chunk_clocks = NoiseLatcherClockDivider; } else { if (EffFreq[ch] >= 2040) { if (chunk_clocks > LatcherClockDivider[ch]) chunk_clocks = LatcherClockDivider[ch]; } else { if (chunk_clocks > FreqCounter[ch]) chunk_clocks = FreqCounter[ch]; } } if (ch == 5 && chunk_clocks > NoiseLatcherClockDivider) chunk_clocks = NoiseLatcherClockDivider; FreqCounter[ch] -= chunk_clocks; while (FreqCounter[ch] <= 0) { if (ch == 5) { int feedback = ((lfsr >> 7) & 1) ^ ((lfsr >> Tap_LUT[(EnvControl[5] >> 12) & 0x7]) & 1) ^ 1; lfsr = ((lfsr << 1) & 0x7FFF) | feedback; FreqCounter[ch] += 10 * (2048 - EffFreq[ch]); } else { FreqCounter[ch] += 2048 - EffFreq[ch]; WavePos[ch] = (WavePos[ch] + 1) & 0x1F; } } LatcherClockDivider[ch] -= chunk_clocks; while (LatcherClockDivider[ch] <= 0) LatcherClockDivider[ch] += 120; if (ch == 5) { NoiseLatcherClockDivider -= chunk_clocks; if (!NoiseLatcherClockDivider) { NoiseLatcherClockDivider = 120; NoiseLatcher = ((lfsr & 1) << 6) - (lfsr & 1); } } EffectsClockDivider[ch] -= chunk_clocks; while (EffectsClockDivider[ch] <= 0) { EffectsClockDivider[ch] += 4800; IntervalClockDivider[ch]--; while (IntervalClockDivider[ch] <= 0) { IntervalClockDivider[ch] += 4; if (IntlControl[ch] & 0x20) { IntervalCounter[ch]--; if (!IntervalCounter[ch]) { IntlControl[ch] &= ~0x80; } } EnvelopeClockDivider[ch]--; while (EnvelopeClockDivider[ch] <= 0) { EnvelopeClockDivider[ch] += 4; if (EnvControl[ch] & 0x0100) // Enveloping enabled? { EnvelopeCounter[ch]--; if (!EnvelopeCounter[ch]) { EnvelopeCounter[ch] = (EnvControl[ch] & 0x7) + 1; if (EnvControl[ch] & 0x0008) // Grow { if (Envelope[ch] < 0xF || (EnvControl[ch] & 0x200)) Envelope[ch] = (Envelope[ch] + 1) & 0xF; } else // Decay { if (Envelope[ch] > 0 || (EnvControl[ch] & 0x200)) Envelope[ch] = (Envelope[ch] - 1) & 0xF; } } } } // end while(EnvelopeClockDivider[ch] <= 0) } // end while(IntervalClockDivider[ch] <= 0) if (ch == 4) { SweepModClockDivider--; while (SweepModClockDivider <= 0) { SweepModClockDivider += (SweepControl & 0x80) ? 8 : 1; if (((SweepControl >> 4) & 0x7) && (EnvControl[ch] & 0x4000)) { if (SweepModCounter) SweepModCounter--; if (!SweepModCounter) { SweepModCounter = (SweepControl >> 4) & 0x7; if (EnvControl[ch] & 0x1000) // Modulation { if (ModWavePos < 32 || (EnvControl[ch] & 0x2000)) { ModWavePos &= 0x1F; EffFreq[ch] = (EffFreq[ch] + (int8)ModData[ModWavePos]); if (EffFreq[ch] < 0) { //puts("Underflow"); EffFreq[ch] = 0; } else if (EffFreq[ch] > 0x7FF) { //puts("Overflow"); EffFreq[ch] = 0x7FF; } ModWavePos++; } //puts("Mod"); } else // Sweep { int32 delta = EffFreq[ch] >> (SweepControl & 0x7); int32 NewFreq = EffFreq[ch] + ((SweepControl & 0x8) ? delta : -delta); //printf("Sweep(%d): Old: %d, New: %d\n", ch, EffFreq[ch], NewFreq); if (NewFreq < 0) EffFreq[ch] = 0; else if (NewFreq > 0x7FF) { //EffFreq[ch] = 0x7FF; IntlControl[ch] &= ~0x80; } else EffFreq[ch] = NewFreq; } } } } // end while(SweepModClockDivider <= 0) } // end if(ch == 4) } // end while(EffectsClockDivider[ch] <= 0) clocks -= chunk_clocks; running_timestamp += chunk_clocks; // Output sound here too. CalcCurrentOutput(ch, left, right); Synth.offset_inline(running_timestamp, left - last_output[ch][0], &sbuf[0]); Synth.offset_inline(running_timestamp, right - last_output[ch][1], &sbuf[1]); last_output[ch][0] = left; last_output[ch][1] = right; } } last_ts = timestamp; //puts("VSU End"); } int32 VSU::EndFrame(int32 timestamp, int16 *SoundBuf, int32 SoundBufMaxSize) { int32 ret = 0; Update(timestamp); last_ts = 0; if (SoundBuf) { for (int y = 0; y < 2; y++) { sbuf[y].end_frame(timestamp); ret = sbuf[y].read_samples(SoundBuf + y, SoundBufMaxSize, 1); } } return ret; } uint8 VSU::PeekWave(const unsigned int which, uint32 Address) { assert(which <= 4); Address &= 0x1F; return (WaveData[which][Address]); } void VSU::PokeWave(const unsigned int which, uint32 Address, uint8 value) { assert(which <= 4); Address &= 0x1F; WaveData[which][Address] = value & 0x3F; } uint8 VSU::PeekModWave(uint32 Address) { Address &= 0x1F; return (ModData[Address]); } void VSU::PokeModWave(uint32 Address, uint8 value) { Address &= 0x1F; ModData[Address] = value & 0xFF; }