/******************************************************************************/ /* Mednafen Sega Saturn Emulation Module */ /******************************************************************************/ /* scsp.inc - SCSP Emulation ** Copyright (C) 2015-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. */ /* TODO: Open bus emulation and cycle timing granularity instead of sample(someday?); be careful with DSP input/output buffering with respect to dry/direct output path delay, and the mapping of the buffers to the address space. Finish MIDI interface emulation. Proper reset/power-on state. Mem4Mb */ SS_SCSP::SS_SCSP() { memset(&RAM[0x40000], 0x00, 0x80000); // Zero out dummy part. Reset(true); } SS_SCSP::~SS_SCSP() { } void SS_SCSP::RecalcSoundInt(void) // Sound CPU interrupts { unsigned mask_test; unsigned lvmasked[3]; unsigned out_level = 0; mask_test = SCIPD & SCIEB; if(mask_test &~ 0xFF) mask_test = (mask_test & 0xFF) | 0x80; lvmasked[0] = (SCILV[0] & mask_test) << 0; lvmasked[1] = (SCILV[1] & mask_test) << 1; lvmasked[2] = (SCILV[2] & mask_test) << 2; for(unsigned i = 0; i < 8; i++) { unsigned l = (lvmasked[0] & 0x1) | (lvmasked[1] & 0x2) | (lvmasked[2] & 0x4); if(l > out_level) out_level = l; lvmasked[0] >>= 1; lvmasked[1] >>= 1; lvmasked[2] >>= 1; } SCSP_SoundIntChanged(out_level); #if 0 if(mask_test) { const unsigned shift = std::min(7, MDFN_tzcount32(mask_test)); unsigned level; level = (((SCILV[0] >> which) & 0x1) << 0) | (((SCILV[1] >> which) & 0x1) << 1) | (((SCILV[2] >> which) & 0x1) << 2); } #endif } void SS_SCSP::RecalcMainInt(void) // Main CPU interrupts { SCSP_MainIntChanged((bool)(MCIPD & MCIEB)); } void SS_SCSP::Reset(bool powering_up) { // // May need to add a DecodeSlotReg() function or something similar if we implement // more aggressive slot register value optimizations on writes in the future. // memset(SlotRegs, 0, sizeof(SlotRegs)); memset(Slots, 0, sizeof(Slots)); // Some SSFs require this; TODO: test to see if this is correct for a reset, // and if not, move it into special SSF loading code. for(unsigned i = 0; i < 32; i++) { Slots[i].EnvLevel = 0x3FF; Slots[i].EnvPhase = ENV_PHASE_RELEASE; } memset(SoundStack, 0, sizeof(SoundStack)); memset(SoundStackDelayer, 0, sizeof(SoundStackDelayer)); if(powering_up) { memset(RAM, 0x00, 0x80000); // or something else? } // // MVOL = 0; MasterVolume = 0; SlotMonitorWhich = 0; SlotMonitorData = 0; DAC18bit = false; Mem4Mb = false; KeyExecute = false; LFSR = 1; GlobalCounter = 0; MIDI.Reset(); // // DMEA = 0; DRGA = 0; DTLG = 0; DMA_Execute = false; DMA_Direction = false; DMA_Gate = false; // // for(unsigned i = 0; i < 3; i++) { Timers[i].Control = 0; Timers[i].Counter = 0; Timers[i].PrevClockIn = false; } // // RBP = 0; RBL = 0; memset(&DSP, 0, sizeof(DSP)); DSP.MDEC_CT = 0; // // SCIEB = 0; SCIPD = 0; MCIEB = 0; MCIPD = 0; for(unsigned i = 0; i < 3; i++) SCILV[i] = 0; RecalcSoundInt(); RecalcMainInt(); } static INLINE void SDL_PAN_ToVolume(int16* outvol, const unsigned level, const unsigned pan) { const bool pan_which = (bool)(pan & 0x10); unsigned basev; unsigned panv; basev = (0x80 << level); if(!level) basev = 0; panv = basev >> ((pan & 0x0F) >> 1); if(pan & 0x01) panv -= (panv >> 2); if((pan & 0x0F) == 0x0F) panv = 0; outvol[ pan_which] = panv; outvol[!pan_which] = basev; } template INLINE void SS_SCSP::RW(uint32 A, T& DBV) { if(A < 0x100000) { if(MDFN_UNLIKELY(A >= 0x80000)) { if(IsWrite) SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] %zu-byte write of value 0x%08x to unmapped SCSP RAM address 0x%06x\n", sizeof(T), DBV, A); else { SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] %zu-byte read from unmapped SCSP RAM address 0x%06x\n", sizeof(T), A); DBV = 0; } } else { ne16_rwbo_be(RAM, A, &DBV); } return; } if(A < 0x100400) { //if(IsWrite) // SS_DBG(SS_DBG_SCSP_REGW, "[SCSP] %zu-byte write to slot 0x%02x register offset 0x%02x: 0x%0*x\n", sizeof(T), (A >> 5) & 0x1F, A & 0x1F, (int)(2 * sizeof(T)), DBV); // // Slot regs // const unsigned slotnum = (A >> 5) & 0x1F; ne16_rwbo_be(SlotRegs[slotnum], A & 0x1F, &DBV); if(IsWrite) { auto* s = &Slots[slotnum]; uint16& SRV = SlotRegs[slotnum][(A >> 1) & 0xF]; switch((A >> 1) & 0xF) { case 0x00: KeyExecute |= (bool)(SRV & 0x1000); SRV &= 0x0FFF; s->KeyBit = (SRV >> 11) & 0x1; s->SBControl = (SRV >> 9) & 0x3; s->SourceControl = (SRV >> 7) & 0x3; s->LoopMode = (SRV >> 5) & 0x3; s->WF8Bit = (SRV >> 4) & 0x1; s->StartAddr = (s->StartAddr & 0xFFFF) | ((SRV & 0xF) << 16); break; case 0x01: s->StartAddr = (s->StartAddr &~ 0xFFFF) | SRV; break; case 0x02: s->LoopStart = SRV; break; case 0x03: s->LoopEnd = SRV; break; case 0x04: s->AttackRate = SRV & 0x1F; s->AttackHold = (SRV >> 5) & 0x1; s->Decay1Rate = (SRV >> 6) & 0x1F; s->Decay2Rate = (SRV >> 11) & 0x1F; break; case 0x05: if(SRV & 0x8000) { //SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] Unknown bits non-zero in slot %u, register %u\n", slotnum, (A >> 1) & 0xF); } s->ReleaseRate = SRV & 0x1F; s->DecayLevel = (SRV >> 5) & 0x1F; s->KRS = (SRV >> 10) & 0xF; s->AttackLoopLink = (SRV >> 14) & 0x1; break; case 0x06: SRV &= 0x0FFF; if(SRV & 0x0C00) { //SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] Unknown bits non-zero in slot %u, register %u\n", slotnum, (A >> 1) & 0xF); } s->TotalLevel = SRV & 0xFF; s->SoundDirect = (SRV >> 8) & 0x1; s->StackWriteInhibit = (SRV >> 9) & 0x1; break; case 0x07: s->ModInputY = SRV & 0x3F; s->ModInputX = (SRV >> 6) & 0x3F; s->ModLevel = (SRV >> 12) & 0xF; break; case 0x08: if(SRV & 0x8400) { //SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] Unknown bits non-zero in slot %u, register %u\n", slotnum, (A >> 1) & 0xF); } s->FreqNum = SRV & 0x7FF; s->Octave = (SRV >> 11) & 0xF; break; case 0x09: s->ALFOModLevel = SRV & 0x7; s->ALFOWaveform = (SRV >> 3) & 0x3; s->PLFOModLevel = (SRV >> 5) & 0x7; s->PLFOWaveform = (SRV >> 8) & 0x3; s->LFOFreq = (SRV >> 10) & 0x1F; s->LFOReset = (SRV >> 15) & 0x1; break; case 0x0A: SRV &= 0x00FF; if(SRV & 0x0080) { //SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] Unknown bits non-zero in slot %u, register %u\n", slotnum, (A >> 1) & 0xF); } s->ToDSPLevel = SRV & 0x7; s->ToDSPSelect = (SRV >> 3) & 0xF; break; case 0x0B: SDL_PAN_ToVolume(s->DirectVolume, (SRV >> 13) & 0x7, (SRV >> 8) & 0x1F); SDL_PAN_ToVolume(s->EffectVolume, (SRV >> 5) & 0x7, (SRV >> 0) & 0x1F); break; case 0x0C: case 0x0D: case 0x0E: case 0x0F: SRV = 0; break; } } return; } if(A < 0x100430) { // OldReg = (OldReg &~ mask) | ((DBV << shift) & mask & whatever); unsigned mask = 0xFFFF; unsigned shift = 0; if(sizeof(T) == 1) { shift = ((A & 1) ^ 1) << 3; mask = 0xFF << shift; } // // Common regs // switch((A >> 1) & 0x1F) { case 0x00: // MVOL (W), DB (W), M4 (W) if(IsWrite) { uint16 tmp = MVOL | (DAC18bit << 8) | (Mem4Mb << 9); tmp = (tmp &~ mask) | ((DBV << shift) & mask); MVOL = (tmp & 0xF); DAC18bit = (tmp >> 8) & 1; Mem4Mb = (tmp >> 9) & 1; // { unsigned mv; mv = 0x2 << (MVOL >> 1); if(!(MVOL & 1)) mv -= (mv >> 2); if(!MVOL) mv = 0; MasterVolume = mv; } } else DBV = 0; break; case 0x01: // RBP (W), RBL (W) if(IsWrite) { uint16 tmp = RBP | (RBL << 7); tmp = (tmp &~ mask) | ((DBV << shift) & mask); RBP = tmp & 0x7F; RBL = (tmp >> 7) & 0x3; } else DBV = 0; break; case 0x02: // MIDI input buffer, Input Empty, Input Full, Input Overflow, Output Empty, Output Full (all R) if(!IsWrite) { if(!shift) { // TODO: Test correct order of flags latching returning versus input fetching/latching(also maybe take into consideration // 16-bit access from SCU being split into 2x 8-bit accesses on the real thing...). unsigned tmp = MIDI.Flags << 8; tmp |= MIDI.ReadInput(); DBV = tmp & mask; } else DBV = MIDI.Flags; } break; case 0x03: // MOBUF (W) if(IsWrite) { } else DBV = 0; break; case 0x04: // CA/SGC/EG (R), MSLC (W) if(IsWrite) { uint16 tmp = (SlotMonitorWhich << 11); tmp = (tmp &~ mask) | ((DBV << shift) & mask); SlotMonitorWhich = (tmp >> 11) & 0x1F; } else { DBV = (SlotMonitorData & mask) >> shift; } break; case 0x05: case 0x06: case 0x07: case 0x08: if(!IsWrite) DBV = 0; break; case 0x09: // DMEA(low) (W) if(IsWrite) { uint16 tmp = DMEA << 1; tmp = (tmp &~ mask) | ((DBV << shift) & mask); DMEA = tmp >> 1; } else DBV = 0; break; case 0x0A: // DRGA (W), DMEA(high) (W) if(IsWrite) { uint16 tmp = ((DMEA >> 3) & 0xF000) | (DRGA << 1); tmp = (tmp &~ mask) | ((DBV << shift) & mask); DMEA = (DMEA & 0x7FFF) | ((tmp & 0xF000) << 3); DRGA = (tmp >> 1) & 0x7FF; } else DBV = 0; break; case 0x0B: // DTLG(W), EX(R/W), DI(R/W), GA(R/W) if(IsWrite) { //const bool prev_execute = DMA_Execute; uint16 tmp = (DTLG << 1) | (DMA_Execute << 12) | (DMA_Direction << 13) | (DMA_Gate << 14); tmp = (tmp &~ mask) | ((DBV << shift) & mask); DTLG = (tmp >> 1) & 0x7FF; DMA_Execute |= (tmp >> 12) & 0x1; DMA_Direction = (tmp >> 13) & 0x1; DMA_Gate = (tmp >> 14) & 0x1; #if 0 if(!prev_execute && DMA_Execute) { printf("[SCSP] DMA Started; Memory Address: 0x%06x, Register Address: 0x%03x, Length: 0x%03x Direction: %u, Gate: %u\n", DMEA << 1, DRGA << 1, DTLG << 1, DMA_Direction, DMA_Gate); } #endif RunDMA(); } else { uint16 tmp = (DMA_Execute << 12) | (DMA_Direction << 13) | (DMA_Gate << 14); DBV = (tmp & mask) >> shift; } break; case 0x0C: // TIMA(W), TACTL(W) case 0x0D: // TIMB(W), TBCTL(W) case 0x0E: // TIMC(W), TCCTL(W) if(IsWrite) { auto* t = &Timers[((A >> 1) & 0x1F) - 0x0C]; uint16 tmp = (t->Control << 8); tmp = (tmp &~ mask) | ((DBV << shift) & mask); t->Control = (tmp >> 8) & 0x7; if(!shift) t->Reload = DBV & 0xFF; //printf("Timer(%zu-byte) %u: %04x\n", sizeof(T), ((A >> 1) & 0x1F) - 0x0C, DBV); } else DBV = 0; break; case 0x0F: // SCIEB (R/W) if(IsWrite) { SCIEB = (SCIEB &~ mask) | ((DBV << shift) & mask & 0x7FF); RecalcSoundInt(); } else DBV = (SCIEB & mask) >> shift; break; case 0x10: // SCIPD (R) (b5 can be written, like MCIPD) if(IsWrite) { SCIPD |= ((DBV << shift) & mask & 0x020); RecalcSoundInt(); } else DBV = (SCIPD & mask) >> shift; break; case 0x11: // SCIRE (W) if(IsWrite) { SCIPD &= ~((DBV << shift) & mask); RecalcSoundInt(); } else DBV = 0; break; case 0x12: // SCILV0 (W) case 0x13: // SCILV1 (W) case 0x14: // SCILV2 (W) if(IsWrite) { const unsigned index = ((A >> 1) & 0x1F) - 0x12; SCILV[index] = (SCILV[index] &~ mask) | ((DBV << shift) & mask & 0x00FF); RecalcSoundInt(); } else DBV = 0; break; case 0x15: // MCIEB (W) if(IsWrite) { MCIEB = (MCIEB &~ mask) | ((DBV << shift) & mask & 0x7FF); RecalcMainInt(); } else DBV = 0; break; case 0x16: // MCIPD (R) (when b5=1 is written, set corresponding bit to 1; writing 0 has no apparent effect) if(IsWrite) { MCIPD |= ((DBV << shift) & mask & 0x020); RecalcMainInt(); } else DBV = (MCIPD & mask) >> shift; break; case 0x17: // MCIRE (W) if(IsWrite) { MCIPD &= ~((DBV << shift) & mask); RecalcMainInt(); } else DBV = 0; break; case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: if(IsWrite) { } else DBV = 0; break; } return; } if(A >= 0x100600 && A <= 0x10067F) { // // Sound stack data // if(IsWrite) *(T*)((uint8*)SoundStack + ((A & 0x7F &~(sizeof(T) - 1)) ^ (2 - sizeof(T)))) = DBV; else DBV = *(T*)((uint8*)SoundStack + ((A & 0x7F & ~(sizeof(T) - 1)) ^ (2 - sizeof(T)))); return; } if(A >= 0x100700 && A <= 0x10077F) { // // DSP coefficients // const unsigned index = (A & 0x7F) >> 1; unsigned mask = 0xFFFF; unsigned shift = 0; if(sizeof(T) == 1) { shift = ((A & 1) ^ 1) << 3; mask = 0xFF << shift; } if(IsWrite) DSP.COEF[index] = (((DSP.COEF[index] << 3) &~ mask) | ((DBV << shift) & mask)) >> 3; else DBV = ((DSP.COEF[index] << 3) & mask) >> shift; return; } if(A >= 0x100780 && A <= 0x1007BF) { // // DSP memory addresses // ne16_rwbo_be(DSP.MADRS, A & 0x3F, &DBV); return; } if(A >= 0x100800 && A <= 0x100BFF) { // // DSP microprogram // ne64_rwbo_be(DSP.MPROG, A & 0x3FF, &DBV); return; } // // DSP work buffer // if(A >= 0x100C00 && A <= 0x100DFF) { const unsigned index = (A & 0x1FF) >> 2; unsigned mask; unsigned shift = (A & 2) ? 8 : 0; if(sizeof(T) == 1) { shift += ((A & 1) ^ 1) << 3; mask = 0xFF << shift; } else mask = 0xFFFF << shift; if(!(A & 2)) mask &= 0xFF; if(IsWrite) DSP.TEMP[index] = (DSP.TEMP[index] &~ mask) | ((DBV << shift) & mask & 0xFFFFFF); else DBV = (DSP.TEMP[index] & mask) >> shift; return; } // // DSP memory read stack // if(A >= 0x100E00 && A <= 0x100E7F) { const unsigned index = (A & 0x7F) >> 2; unsigned mask; unsigned shift = (A & 2) ? 8 : 0; if(sizeof(T) == 1) { shift += ((A & 1) ^ 1) << 3; mask = 0xFF << shift; } else mask = 0xFFFF << shift; if(!(A & 2)) mask &= 0xFF; if(IsWrite) DSP.MEMS[index] = (DSP.MEMS[index] &~ mask) | ((DBV << shift) & mask & 0xFFFFFF); else DBV = (DSP.MEMS[index] & mask) >> shift; return; } // // DSP mix stack // if(A >= 0x100E80 && A <= 0x100EBF) { const unsigned index = (A & 0x3F) >> 2; unsigned mask; unsigned shift = (A & 2) ? 4 : 0; if(sizeof(T) == 1) { shift += ((A & 1) ^ 1) << 3; mask = 0xFF << shift; } else mask = 0xFFFF << shift; if(!(A & 2)) mask &= 0x0F; if(IsWrite) DSP.MIXS[index] = (DSP.MIXS[index] &~ mask) | ((DBV << shift) & mask & 0xFFFFFF); else DBV = (DSP.MIXS[index] & mask) >> shift; return; } // // // if(A >= 0x100EC0 && A <= 0x100EDF) { ne16_rwbo_be(DSP.EFREG, A & 0x1F, &DBV); return; } if(A >= 0x100EE0 && A <= 0x100EE3) { if(!IsWrite) DBV = ne16_rbo_be(EXTS, A & 0x3); return; } if(IsWrite) SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] Unknown %zu-byte write of value 0x%08x to address 0x%08x\n", sizeof(T), DBV, A); else { SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] Unknown %zu-byte read from address 0x%08x\n", sizeof(T), A); DBV = 0; } } // // Gate bit only forces the value to 0, the read still seems to occur(or at the very least timing side effects occur for the case of reg->mem). // void NO_INLINE SS_SCSP::RunDMA(void) { if(!DMA_Execute) return; uint32 length = DTLG; uint32 mem_addr = DMEA; uint32 reg_addr = DRGA; bool dir = DMA_Direction; bool gate = DMA_Gate; while(length) { if(dir) { uint16 tmp; RW(0x100000 | (reg_addr << 1), tmp); if(gate) tmp = 0; if(MDFN_LIKELY(mem_addr < 0x40000)) RAM[mem_addr] = tmp; } else { uint16 tmp = RAM[mem_addr]; if(gate) tmp = 0; RW(0x100000 | (reg_addr << 1), tmp); } reg_addr = (reg_addr + 1) & 0x000007FF; mem_addr = (mem_addr + 1) & 0x0007FFFF; length = length - 1; } DMA_Execute = false; SCIPD |= 0x10; MCIPD |= 0x10; RecalcSoundInt(); RecalcMainInt(); } INLINE void SS_SCSP::RunEG(Slot* s, const unsigned key_eg_scale) { if(s->EnvPhase == ENV_PHASE_DECAY1 && (s->EnvLevel >> 5) == s->DecayLevel) s->EnvPhase = ENV_PHASE_DECAY2; // // // bool ClockEG; const unsigned ERateNoScale = s->EnvRates[s->EnvPhase]; const unsigned ERate = std::min(0x1F, key_eg_scale + ERateNoScale); const unsigned ERateWBT = (0x22 - std::min(0x18, ERate)) >> 1; const bool EGCBT = (GlobalCounter >> ERateWBT) & 1; ClockEG = !s->EnvGCBTPrev && EGCBT; if((ERate < 0x18) && (ERate & 1)) ClockEG &= (bool)((GlobalCounter >> (ERateWBT + 1)) & 0x3); ClockEG &= (bool)ERateNoScale; s->EnvGCBTPrev = EGCBT; if(ClockEG) { const int32 inc_base = (s->EnvPhase == ENV_PHASE_ATTACK) ? ~s->EnvLevel : 16; const unsigned ermaxo = std::max(0x18, std::min(0x1E, ERate)); const uint32 srac = ((0x20 - ermaxo) >> 1) + (ermaxo & 1 & (GlobalCounter >> (ERateWBT + 1))); //if(s == &Slots[0] && s->EnvLevel) // printf("EP: %u, EL: 0x%04x, AR: 0x%02x, %d, %u --- %02x\n", s->EnvPhase, s->EnvLevel, s->AttackRate, inc_base, srac, ERate); s->EnvLevel += inc_base >> srac; if((int32)s->EnvLevel >= 0x3FF) { s->WFAllowAccess = false; s->EnvLevel = 0x3FF; } if((int32)s->EnvLevel < 0) s->EnvLevel = 0; } // // // if(s->EnvPhase == ENV_PHASE_ATTACK) { if((s->AttackLoopLink && s->InLoop) || (!s->AttackLoopLink && s->EnvLevel == 0)) s->EnvPhase = ENV_PHASE_DECAY1; } } // // Take care in handling LFSR, or else the volume of noise-ALFO-modulated noise will // be quite off, or have a DC bias. // INLINE uint8 SS_SCSP::GetALFO(Slot* s) { uint8 ret; switch(s->ALFOWaveform) { default: case 0: // Saw ret = s->LFOCounter &~ 1; break; case 1: // Square ret = ((int8)s->LFOCounter >> 7) &~ 1; break; case 2: // Triangle ret = (unsigned)(s->LFOCounter ^ ((int8)s->LFOCounter >> 7)) << 1; break; case 3: // Noise ret = LFSR &~ 1; break; } ret >>= (7 - s->ALFOModLevel); if(!s->ALFOModLevel) ret = 0; return ret; } INLINE int SS_SCSP::GetPLFO(Slot* s) { int ret; switch(s->PLFOWaveform) { default: case 0: // Saw ret = (int8)(s->LFOCounter &~ 1); break; case 1: // Square ret = (int8)((s->LFOCounter & 0x80) ? 0x80 : 0x7E); break; case 2: // Triangle ret = (int8)(((s->LFOCounter & 0x3F) ^ ((s->LFOCounter & 0x40) ? 0x3F : 0x00) ^ ((s->LFOCounter & 0x80) ? 0x7F : 0x00)) << 1); break; case 3: // Noise ret = (int8)(LFSR &~ 1); break; } ret >>= (7 - s->PLFOModLevel); if(!s->PLFOModLevel) ret = 0; ret = ((0x40 ^ (s->FreqNum >> 4)) * ret) >> 6; return ret; } INLINE void SS_SCSP::RunLFO(Slot* s) { s->LFOTimeCounter--; if(s->LFOTimeCounter <= 0) { s->LFOCounter++; s->LFOTimeCounter = (((8 - (s->LFOFreq & 0x3)) << 7) >> (s->LFOFreq >> 2)) - 4; } if(s->LFOReset) s->LFOCounter = 0; } // // // static INLINE uint32 dspfloat_to_int(const uint16 inv) { const uint32 sign_xor = (int32)((inv & 0x8000) << 16) >> 1; const uint32 exp = (inv >> 11) & 0xF; uint32 ret; ret = inv & 0x7FF; if(exp < 12) ret |= 0x800; ret <<= 11 + 8; ret ^= sign_xor; ret = (int32)ret >> (8 + std::min(11, exp)); return ret & 0xFFFFFF; } static INLINE uint32 int_to_dspfloat(const uint32 inv) { const uint32 invsl8 = inv << 8; const uint32 sign_xor = (int)invsl8 >> 31; uint32 exp, shift; uint32 ret; exp = MDFN_lzcount32(((invsl8 ^ sign_xor) << 1) | (1 << 19)); shift = exp - (bool)(exp == 12); //std::min(11, exp); ret = (int32)invsl8 >> (19 - shift); ret &= 0x87FF; ret |= exp << 11; return ret; } static const uint16 SB_XOR_Table[4] = { 0x0000, 0x7FFF, 0x8000, 0xFFFF }; INLINE void SS_SCSP::RunDSP(void) { // // // Instruction field order/width RE'ing notes: // // Bit 0: NXADDR // Bit 1: ADRGB // Bit 2-6: MASA // Bit 8: NOFL (disables floating-point conversion when =1, instead just shifting by 8); has effect with MRT=1 or MWT=1 // Bit 9-14: CRA (Coefficient read address, input into Y_SEL) // Bit 16: BSEL // Bit 17: ZERO // Bit 18: NEGB (apparently no effect when ZERO=1) // Bit 19: YRL // Bit 20: SHFT0 // Bit 21: SHFT1 // Bit 22: FRCL // Bit 23: ADRL (latches A_SEL output into ADRS_REG) // Bit 24-27: EWA(EFREG write address) // Bit 28: EWT(EFREG write enable) // Bit 29: MRT (Memory read trigger; to read: [MWR=1] [whatever instruction] [IWT=1] // Bit 30: MWT (Memory write trigger) // Bit 31: TABLE // Bit 32-36: IWA (MEMS write address) // Bit 37: IWT (MEMS write trigger) // Bit 38-43: IRA (0x00-0x1F MEMS, 0x20-0x2F MIXS) // Bit 45-46: YSEL // Bit 47: XSEL // Bit 48-54: TWA(temp write address) Seems to be an offset added to a counter changed each sample. // Bit 55: TWT(temp write trigger) WARNING: Setting this to 1 for all 128 steps apparently can cause a CPU to freeze up if it tries to read/write TEMP afterward. // Bit 56-62: TRA(temp read address) for(unsigned step = 0; step < 128; step++) { const uint64 instr = DSP.MPROG[step]; /* assert(!(instr & (1ULL << 7))); assert(!(instr & (1ULL << 15))); assert(!(instr & (1ULL << 44))); assert(!(instr & (1ULL << 63))); */ const bool NXADDR = (instr >> 0) & 1; const bool ADRGB = (instr >> 1) & 1; const unsigned MASA = (instr >> 2) & 0x1F; const bool NOFL = (instr >> 8) & 1; const unsigned CRA = (instr >> 9) & 0x3F; const bool BSEL = (instr >> 16) & 1; const bool ZERO = (instr >> 17) & 1; const bool NEGB = (instr >> 18) & 1; const bool YRL = (instr >> 19) & 1; const bool SHFT0 = (instr >> 20) & 1; const bool SHFT1 = (instr >> 21) & 1; const bool FRCL = (instr >> 22) & 1; const bool ADRL = (instr >> 23) & 1; const unsigned EWA = (instr >> 24) & 0x0F; const bool EWT = (instr >> 28) & 1; const bool MRT = (instr >> 29) & 1; const bool MWT = (instr >> 30) & 1; const bool TABLE = (instr >> 31) & 1; const unsigned IWA = (instr >> 32) & 0x1F; const bool IWT = (instr >> 37) & 1; const unsigned IRA = (instr >> 38) & 0x3F; const unsigned YSEL = (instr >> 45) & 0x03; const bool XSEL = (instr >> 47) & 1; const unsigned TEMPWriteAddr = ((instr >> 48) + DSP.MDEC_CT) & 0x7F; const bool TWT = (instr >> 55) & 1; const unsigned TEMPReadAddr = ((instr >> 56) + DSP.MDEC_CT) & 0x7F; #if 0 if(!(step & 1) && (MWT || MRT)) SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] Memory access requested at even DSP step %u; 0x%016llx\n", step, instr); if(MWT & MRT) SS_DBG(SS_DBG_WARNING | SS_DBG_SCSP, "[SCSP] MWT and MRT both 1 at DSP step %u; 0x%016llx\n", step, instr); #endif // // if(IRA & 0x20) { if(IRA & 0x10) { if(!(IRA & 0xE)) DSP.INPUTS = EXTS[IRA & 0x1] << 8; } else { DSP.INPUTS = DSP.MIXS[IRA & 0xF] << 4; } } else { DSP.INPUTS = DSP.MEMS[IRA & 0x1F]; } const int32 INPUTS = sign_x_to_s32(24, DSP.INPUTS); const int32 TEMP = sign_x_to_s32(24, DSP.TEMP[TEMPReadAddr]); const int32 X_SEL_Inputs[2] = { TEMP, INPUTS }; const uint16 Y_SEL_Inputs[4] = { DSP.FRC_REG, DSP.COEF[CRA], (uint16)((DSP.Y_REG >> 11) & 0x1FFF), (uint16)((DSP.Y_REG >> 4) & 0x0FFF) }; const uint32 SGA_Inputs[2] = { (uint32)TEMP, DSP.SFT_REG }; // // // if(YRL) { DSP.Y_REG = INPUTS & 0xFFFFFF; } // // // int32 ShifterOutput; ShifterOutput = (uint32)sign_x_to_s32(26, DSP.SFT_REG) << (SHFT0 ^ SHFT1); if(!SHFT1) { if(ShifterOutput > 0x7FFFFF) ShifterOutput = 0x7FFFFF; else if(ShifterOutput < -0x800000) ShifterOutput = 0x800000; } ShifterOutput &= 0xFFFFFF; if(EWT) DSP.EFREG[EWA] = (ShifterOutput >> 8); if(TWT) DSP.TEMP[TEMPWriteAddr] = ShifterOutput; // // if(FRCL) { const unsigned F_SEL_Inputs[2] = { (unsigned)(ShifterOutput >> 11), (unsigned)(ShifterOutput & 0xFFF) }; DSP.FRC_REG = F_SEL_Inputs[SHFT0 & SHFT1]; //printf("FRCL: 0x%08x\n", DSP.FRC_REG); } // // DSP.Product = ((int64)sign_x_to_s32(13, Y_SEL_Inputs[YSEL]) * X_SEL_Inputs[XSEL]) >> 12; // if(step < 4) // printf("%u %08x %08x product=0x%08x\n", step, Y_SEL_Inputs[YSEL], X_SEL_Inputs[XSEL], DSP.Product); // // //if((step == 3 || step == 7) && CRA) // printf("Step %u: %08x %08x %08x --- ysel=%u cra=0x%02x[0x%04x] temp=0x%08x\n", step, SGA_Inputs[BSEL], Y_SEL_Inputs[YSEL], X_SEL_Inputs[XSEL], YSEL, CRA, DSP.COEF[CRA], TEMP); uint32 SGAOutput; SGAOutput = SGA_Inputs[BSEL]; if(NEGB) SGAOutput = -SGAOutput; if(ZERO) SGAOutput = 0; DSP.SFT_REG = (DSP.Product + SGAOutput) & 0x3FFFFFF; // // if(IWT) { DSP.MEMS[IWA] = DSP.ReadValue; } // // if(DSP.ReadPending) { uint16 tmp = RAM[DSP.RWAddr]; DSP.ReadValue = (DSP.ReadPending == 2) ? (tmp << 8) : dspfloat_to_int(tmp); DSP.ReadPending = false; } else if(DSP.WritePending) { if(!(DSP.RWAddr & 0x40000)) RAM[DSP.RWAddr] = DSP.WriteValue; DSP.WritePending = false; } { uint16 addr; addr = DSP.MADRS[MASA]; addr += NXADDR; if(ADRGB) { addr += sign_x_to_s32(12, DSP.ADRS_REG); } if(!TABLE) { addr += DSP.MDEC_CT; addr &= (0x2000 << RBL) - 1; } DSP.RWAddr = (addr + (RBP << 12)) & 0x7FFFF; if(MRT) { DSP.ReadPending = 1 + NOFL; } if(MWT) { DSP.WritePending = true; DSP.WriteValue = NOFL ? (ShifterOutput >> 8) : int_to_dspfloat(ShifterOutput); } } // // if(ADRL) { const uint16 A_SEL_Inputs[2] = { /*INPUTS is sign-extended above */ (uint16)((INPUTS >> 16) & 0xFFF), (uint16)(ShifterOutput >> 12) }; DSP.ADRS_REG = A_SEL_Inputs[SHFT0 & SHFT1]; } } if(!DSP.MDEC_CT) DSP.MDEC_CT = (0x2000 << RBL); DSP.MDEC_CT--; } // // // INLINE void SS_SCSP::RunSample(int16* outlr) { int32 out_accum[2] = { 0, 0 }; for(unsigned i = 0; i < 3; i++) { auto* t = &Timers[i]; bool CCB = (GlobalCounter >> (4 + t->Control)) & 1; bool DoClock = (t->Control == 0) || (!t->PrevClockIn && CCB); t->PrevClockIn = CCB; if(DoClock) { if(t->Reload >= 0) { t->Counter = t->Reload; t->Reload = -1; } else t->Counter++; if(t->Counter == 0xFF) { SCIPD |= 0x40 << i; MCIPD |= 0x40 << i; } } } SCIPD |= 0x400; MCIPD |= 0x400; RecalcSoundInt(); RecalcMainInt(); // // // RunDSP(); for(unsigned i = 0; i < 0x10; i++) DSP.MIXS[i] = 0; // // // for(unsigned slot = 0; slot < 32; slot++) { uint32 mdata = 0; auto* s = &Slots[slot]; unsigned key_eg_scale; if(s->KRS == 0xF) key_eg_scale = 0x00; else key_eg_scale = std::max(0x00, std::min(0x0F, s->KRS + (s->Octave ^ 0x8) - 0x8)); RunEG(s, key_eg_scale); if(KeyExecute && (s->EnvPhase == ENV_PHASE_RELEASE) == s->KeyBit) { if(s->KeyBit) { s->PhaseWhacker = 0; s->CurrentAddr = 0; s->InLoop = false; s->LoopSub = false; s->WFAllowAccess = true; s->EnvPhase = ENV_PHASE_ATTACK; if((s->AttackRate + key_eg_scale) >= 0x20) s->EnvLevel = 0x000; else s->EnvLevel = 0x280; } else s->EnvPhase = ENV_PHASE_RELEASE; } // // uint16 sample = 0; if(s->SourceControl == 1) sample = LFSR << 8; sample ^= SB_XOR_Table[s->SBControl]; // For zero and noise case only; waveform playback needs it to occur before linear interpolation. if(1) //s->WFAllowAccess) { if(!s->InLoop) { if((uint16)(s->CurrentAddr + 1) > s->LoopStart) { if(s->LoopMode == 2) s->LoopSub = true; s->InLoop = true; } } else { const bool cres = s->LoopSub ? ((uint16)(s->LoopEnd - s->CurrentAddr + s->LoopStart) <= s->LoopStart) : ((uint16)(s->CurrentAddr + 1) > s->LoopEnd); if(cres) { if(s->LoopMode == 0) s->WFAllowAccess = false; if(s->LoopMode == 3) s->LoopSub = !s->LoopSub; s->CurrentAddr += s->LoopStart - s->LoopEnd; } } } if(s->WFAllowAccess) { uint32 modalizer; uint32 tmppw = s->PhaseWhacker; uint16 tmpa = s->CurrentAddr; int16 s0, s1; // // modalizer = (int16)SoundStack[(GlobalCounter + s->ModInputX) & 0x3F]; modalizer += (int16)SoundStack[(GlobalCounter + s->ModInputY) & 0x3F]; modalizer >>= 0x10 - s->ModLevel; if(s->ModLevel <= 0x04) modalizer = 0; modalizer = sign_x_to_s32(11, modalizer); // // if(s->LoopSub) { tmppw = ~tmppw; tmpa = s->LoopStart + s->LoopEnd + ~tmpa; } mdata |= ((tmpa >> 12) << 7); if(s->WF8Bit) { const uint32 addr0 = (s->StartAddr + modalizer + (uint16)(tmpa + 0)) & 0xFFFFF; const uint32 addr1 = (s->StartAddr + modalizer + (uint16)(tmpa + 1)) & 0xFFFFF; s0 = ne16_rbo_be(RAM, addr0) << 8; s1 = ne16_rbo_be(RAM, addr1) << 8; } else { s0 = RAM[((s->StartAddr >> 1) + modalizer + (uint16)(tmpa + 0)) & 0x7FFFF]; s1 = RAM[((s->StartAddr >> 1) + modalizer + (uint16)(tmpa + 1)) & 0x7FFFF]; } s0 ^= SB_XOR_Table[s->SBControl]; s1 ^= SB_XOR_Table[s->SBControl]; if(s->SourceControl == 0) { const unsigned sia = (tmppw >> (14 - 6)) & 0x3F; sample = ((s0 * (0x40 - sia)) + (s1 * sia)) >> 6; } s->PhaseWhacker += (((0x400 ^ s->FreqNum) + GetPLFO(s)) << (s->Octave ^ 0x8)) >> 4; s->CurrentAddr += s->PhaseWhacker >> 14; s->PhaseWhacker &= (1U << 14) - 1; } // // RunLFO(s); // Run between PLFO fetching and ALFO fetching. // Do LFSR clocking between sample fetching and ALFO fetching. LFSR = (LFSR >> 1) | (((LFSR >> 5) ^ LFSR) & 1) << 16; { int32 vlevel; vlevel = (s->EnvPhase == ENV_PHASE_ATTACK && s->AttackHold) ? 0 : s->EnvLevel; // mdata |= (s->EnvPhase << 5) | (vlevel >> 5); // if(!s->SoundDirect) { vlevel += s->TotalLevel << 2; vlevel += GetALFO(s); if(vlevel > 0x3FF) vlevel = 0x3FF; sample = ((int16)sample * ((vlevel & 0x3F) ^ 0x7F)) >> ((vlevel >> 6) + 7); } } if(!Slots[(GlobalCounter - 4) & 0x1F].StackWriteInhibit) { SoundStack[(GlobalCounter - 4) & 0x3F] = SoundStackDelayer[3]; } SoundStackDelayer[3] = SoundStackDelayer[2]; SoundStackDelayer[2] = SoundStackDelayer[1]; SoundStackDelayer[1] = SoundStackDelayer[0]; SoundStackDelayer[0] = sample; // // if(SlotMonitorWhich == slot) SlotMonitorData = mdata; // // if(s->ToDSPLevel) DSP.MIXS[s->ToDSPSelect] = (DSP.MIXS[s->ToDSPSelect] + (((uint32)(int16)sample << 4) >> (7 - s->ToDSPLevel))) & 0xFFFFF; // // out_accum[0] += ((int16)sample * s->DirectVolume[0]) >> 14; out_accum[1] += ((int16)sample * s->DirectVolume[1]) >> 14; { const uint16 eff_sample = (slot & 0x10) ? ((slot & 0xE) ? 0 : EXTS[slot & 0x1]) : DSP.EFREG[slot]; out_accum[0] += ((int16)eff_sample * s->EffectVolume[0]) >> 14; out_accum[1] += ((int16)eff_sample * s->EffectVolume[1]) >> 14; } // // GlobalCounter++; } KeyExecute = false; // // // out_accum[0] = (out_accum[0] * MasterVolume) >> 8; out_accum[1] = (out_accum[1] * MasterVolume) >> 8; out_accum[0] = std::min(32767, std::max(-32768, out_accum[0])); out_accum[1] = std::min(32767, std::max(-32768, out_accum[1])); if(DAC18bit) { // Doesn't seem to improve precision. Remember // to extend the outlr[] types if this SCSP emulator is used // in a system that actually has an 18-bit DAC. out_accum[0] = (uint32)out_accum[0] << 2; out_accum[1] = (uint32)out_accum[1] << 2; } outlr[0] = out_accum[0]; outlr[1] = out_accum[1]; } // // // uint32 SS_SCSP::GetRegister(const unsigned id, char* const special, const uint32 special_len) { uint32 ret = 0xDEADBEEF; switch(id) { case GSREG_MVOL: ret = MVOL; break; case GSREG_DAC18B: ret = DAC18bit; break; case GSREG_MEM4MB: ret = Mem4Mb; break; case GSREG_RBP: ret = RBP; break; case GSREG_RBL: ret = RBL; break; case GSREG_MSLC: ret = SlotMonitorWhich; break; } return ret; } void SS_SCSP::SetRegister(const unsigned id, const uint32 value) { switch(id) { //case GSREG_MVOL: MVOL = value & 0xF; // TODO cache case GSREG_DAC18B: DAC18bit = value & 1; break; case GSREG_MEM4MB: Mem4Mb = value & 1; break; case GSREG_RBP: RBP = value & 0x7F; break; case GSREG_RBL: RBL = value & 0x3; break; case GSREG_MSLC: SlotMonitorWhich = value & 0x1F; break; } }