void PCE_PSG::SetRegister(const unsigned int id, const uint32 value) { const int ch = (id >> 8) & 0xF; switch(id & 0xF0FF) { default: break; case PSG_GSREG_SELECT: select = value & 0x07; break; case PSG_GSREG_GBALANCE: globalbalance = value & 0xFF; break; case PSG_GSREG_LFOFREQ: lfofreq = value & 0xFF; break; case PSG_GSREG_LFOCTRL: lfoctrl = value & 0x83; RecalcFreqCache(0); RecalcUOFunc(0); RecalcFreqCache(1); RecalcUOFunc(1); break; case PSG_GSREG_CH0_FREQ: channel[ch].frequency = value & 0xFFF; RecalcFreqCache(ch); RecalcUOFunc(ch); break; case PSG_GSREG_CH0_CTRL: channel[ch].control = value & 0xFF; RecalcFreqCache(ch); RecalcUOFunc(ch); break; case PSG_GSREG_CH0_BALANCE: channel[ch].balance = value & 0xFF; break; case PSG_GSREG_CH0_WINDEX: channel[ch].waveform_index = value & 0x1F; break; case PSG_GSREG_CH0_SCACHE: channel[ch].dda = value & 0x1F; break; case PSG_GSREG_CH0_NCTRL: channel[ch].noisectrl = value & 0xFF; RecalcNoiseFreqCache(ch); RecalcUOFunc(ch); break; case PSG_GSREG_CH0_LFSR: channel[ch].lfsr = value & 0x7FFF; break; } }
void PCEFast_PSG::RunChannel(int chc, int32 timestamp) { psg_channel *ch = &channel[chc]; int32 running_timestamp = ch->lastts; int32 run_time = timestamp - ch->lastts; ch->lastts = timestamp; if(!run_time) return; (this->*ch->UpdateOutput)(running_timestamp, ch); if(chc >= 4) { int32 freq = ch->noise_freq_cache; ch->noisecount -= run_time; #define CLOCK_LFSR(lfsr) { unsigned int newbit = ((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 11) ^ (lfsr >> 12) ^ (lfsr >> 17)) & 1; lfsr = (lfsr >> 1) | (newbit << 17); } if(&PCEFast_PSG::UpdateOutput_Noise == ch->UpdateOutput) { while(ch->noisecount <= 0) { CLOCK_LFSR(ch->lfsr); UpdateOutput_Noise(timestamp + ch->noisecount, ch); ch->noisecount += freq; } } else { while(ch->noisecount <= 0) { CLOCK_LFSR(ch->lfsr); ch->noisecount += freq; } } #undef CLOCK_LFSR } // D7 of control is 0, don't clock the counter at all. // D7 of lfocontrol is 1(and chc == 1), don't clock the counter at all(not sure about this) // In DDA mode, don't clock the counter. // (Noise being enabled isn't handled here since AFAIK it doesn't disable clocking of the waveform portion, its sound just overrides the sound from // the waveform portion when the noise enable bit is set, which is handled in our RecalcUOFunc). if(!(ch->control & 0x80) || (chc == 1 && (lfoctrl & 0x80)) || (ch->control & 0x40)) return; ch->counter -= run_time; if(!LFO_On && ch->freq_cache <= 0xA) { if(ch->counter <= 0) { const int32 inc_count = ((0 - ch->counter) / ch->freq_cache) + 1; ch->counter += inc_count * ch->freq_cache; ch->waveform_index = (ch->waveform_index + inc_count) & 0x1F; ch->dda = ch->waveform[ch->waveform_index]; } } while(ch->counter <= 0) { ch->waveform_index = (ch->waveform_index + 1) & 0x1F; ch->dda = ch->waveform[ch->waveform_index]; (this->*ch->UpdateOutput)(timestamp + ch->counter, ch); if(LFO_On) { RunChannel<false>(1, timestamp + ch->counter); RecalcFreqCache(0); RecalcUOFunc(0); ch->counter += (ch->freq_cache <= 0xA) ? 0xA : ch->freq_cache; // Not particularly accurate, but faster. } else ch->counter += ch->freq_cache; } }
void PCEFast_PSG::Update(int32 timestamp) { int32 run_time = timestamp - lastts; if(vol_pending && !vol_update_counter && !vol_update_which) { vol_update_counter = 1; vol_pending = false; } bool lfo_on = (bool)(lfoctrl & 0x03); if(lfo_on) { if(!(channel[1].control & 0x80) || (lfoctrl & 0x80)) { lfo_on = 0; RecalcFreqCache(0); RecalcUOFunc(0); } } int32 clocks = run_time; int32 running_timestamp = lastts; while(clocks > 0) { int32 chunk_clocks = clocks; if(vol_update_counter > 0 && chunk_clocks > vol_update_counter) chunk_clocks = vol_update_counter; running_timestamp += chunk_clocks; clocks -= chunk_clocks; if(lfo_on) UpdateSubLFO(running_timestamp); else UpdateSubNonLFO(running_timestamp); if(vol_update_counter > 0) { vol_update_counter -= chunk_clocks; if(!vol_update_counter) { const int phase = vol_update_which & 1; const int lr = ((vol_update_which >> 1) & 1) ^ 1; const int chnum = vol_update_which >> 2; if(!phase) { //printf("Volume update(Read, %d since last): ch=%d, lr=%d, ts=%d\n", running_timestamp - last_read, chnum, lr, running_timestamp); if(chnum < 6) { vol_update_vllatch = GetVL(chnum, lr); } //last_read = running_timestamp; } else { // printf("Volume update(Apply): ch=%d, lr=%d, ts=%d\n", chnum, lr, running_timestamp); if(chnum < 6) { channel[chnum].vl[lr] = vol_update_vllatch; } //last_apply = running_timestamp; } vol_update_which = (vol_update_which + 1) & 0x1F; if(vol_update_which) vol_update_counter = phase ? 1 : 255; else if(vol_pending) { vol_update_counter = phase ? 1 : 255; vol_pending = false; } } } lastts = running_timestamp; }
void PCEFast_PSG::Write(int32 timestamp, uint8 A, uint8 V) { A &= 0x0F; if(A == 0x00) { select = (V & 0x07); return; } Update(timestamp); psg_channel *ch = &channel[select]; //if(A == 0x01 || select == 5) // printf("Write Ch: %d %04x %02x, %d\n", select, A, V, timestamp); switch(A) { default: break; case 0x01: /* Global sound balance */ globalbalance = V; vol_pending = true; break; case 0x02: /* Channel frequency (LSB) */ if(select > 5) return; // no more than 6 channels, silly game. ch->frequency = (ch->frequency & 0x0F00) | V; RecalcFreqCache(select); RecalcUOFunc(select); break; case 0x03: /* Channel frequency (MSB) */ if(select > 5) return; // no more than 6 channels, silly game. ch->frequency = (ch->frequency & 0x00FF) | ((V & 0x0F) << 8); RecalcFreqCache(select); RecalcUOFunc(select); break; case 0x04: /* Channel enable, DDA, volume */ if(select > 5) return; // no more than 6 channels, silly game. if((ch->control & 0x40) && !(V & 0x40)) { ch->waveform_index = 0; ch->dda = ch->waveform[ch->waveform_index]; ch->counter = ch->freq_cache; } if(!(ch->control & 0x80) && (V & 0x80)) { if(!(V & 0x40)) { ch->waveform_index = (ch->waveform_index + 1) & 0x1F; ch->dda = ch->waveform[ch->waveform_index]; } } ch->control = V; RecalcFreqCache(select); RecalcUOFunc(select); vol_pending = true; break; case 0x05: /* Channel balance */ if(select > 5) return; // no more than 6 channels, silly game. ch->balance = V; vol_pending = true; break; case 0x06: /* Channel waveform data */ if(select > 5) return; // no more than 6 channels, silly game. V &= 0x1F; if(!(ch->control & 0x40)) { ch->samp_accum -= ch->waveform[ch->waveform_index]; ch->waveform[ch->waveform_index] = V; ch->samp_accum += ch->waveform[ch->waveform_index]; } if((ch->control & 0xC0) == 0x00) ch->waveform_index = ((ch->waveform_index + 1) & 0x1F); if(ch->control & 0x80) { // According to my tests(on SuperGrafx), writing to this channel // will update the waveform value cache/latch regardless of DDA mode being enabled. ch->dda = V; } break; case 0x07: /* Noise enable and frequency */ if(select > 5) return; // no more than 6 channels, silly game. if(select >= 4) { ch->noisectrl = V; RecalcNoiseFreqCache(select); RecalcUOFunc(select); } break; case 0x08: /* LFO frequency */ lfofreq = V & 0xFF; //printf("LFO Freq: %02x\n", V); break; case 0x09: /* LFO trigger and control */ //printf("LFO Ctrl: %02x\n", V); if(V & 0x80) { channel[1].waveform_index = 0; channel[1].dda = channel[1].waveform[channel[1].waveform_index]; channel[1].counter = channel[1].freq_cache; } lfoctrl = V; RecalcFreqCache(0); RecalcUOFunc(0); RecalcFreqCache(1); RecalcUOFunc(1); break; } }