diff --git a/core/vb.c b/core/vb.c index c73497f..d424a00 100644 --- a/core/vb.c +++ b/core/vb.c @@ -64,7 +64,9 @@ typedef struct { } wave; /* Other state */ - uint32_t clocks; /* Clocks until next sample */ + uint32_t clocks; /* Clocks until next wave or noise sample */ + uint8_t freqmod; /* Frequency modifications are active */ + uint32_t until; /* Clocks until channel component update */ } Channel; /* Simulation state */ diff --git a/core/vsu.c b/core/vsu.c index 706564b..10c4777 100644 --- a/core/vsu.c +++ b/core/vsu.c @@ -74,100 +74,57 @@ static void vsuNextFreqMod(VB *sim, Channel *chan) { } /* Process one channel */ -static void vsuEmulateChannel(VB *sim, int index, uint32_t clocks) { - uint32_t bit; /* Pseudorandom bit */ - Channel *chan; /* Channel handle */ - int freqmod; /* Frequency modifications enabled */ - uint32_t until; /* Clocks to process sub-channel components */ +static void vsuEmulateChannel(VB *sim, Channel *chan, uint32_t clocks) { + uint32_t bit; /* Pseudorandom bit */ - /* Select channel */ - chan = &sim->vsu.channels[index]; - - /* Channel is disabled */ - if (!chan->int_.enb) + /* Automatic shutoff */ + if (chan->int_.auto_ && chan->int_.clocks == 0) { + chan->int_.enb = 0; return; + } - /* Frequency modifications are active */ - freqmod = - index == 4 && /* Channel 5 */ - sim->vsu.freqmod.enb && /* Modifications enabled */ - sim->vsu.freqmod.interval != 0 /* Modifications valid */ - ; + /* Next input sample */ + if (chan->clocks == 0) { - /* Process all clocks */ - do { + /* Wave */ + if (chan != &sim->vsu.channels[5]) { + chan->clocks = 4 * (2048 - (uint32_t) chan->freq.current); + chan->wave.sample = (chan->wave.sample + 1) & 31; + } - /* Clocks until next state change */ - until = clocks; - if (chan->clocks < until) - until = chan->clocks; - if (chan->env.enb && chan->env.clocks < until) - until = chan->env.clocks; - if (chan->int_.auto_ && chan->int_.clocks < until) - until = chan->int_.clocks; - if (freqmod && sim->vsu.freqmod.clocks < until) - until = sim->vsu.freqmod.clocks; + /* Noise */ + else { + chan->clocks = 40 * (2048 - (uint32_t) chan->freq.current); + bit = (( + sim->vsu.noise.register_ >> NOISE_TAPS[sim->vsu.noise.tap]^ + sim->vsu.noise.register_ >> 7 + ) & 1) ^ 1; + sim->vsu.noise.register_ = bit | + (sim->vsu.noise.register_ << 1 & 0x7FFE); + } - /* Manage clocks */ - clocks -= until; - chan->clocks -= until; - if (chan->env.enb) - chan->env.clocks -= until; - if (chan->int_.auto_) - chan->int_.clocks -= until; - if (freqmod) - sim->vsu.freqmod.clocks -= until; + } - /* Automatic shutoff */ - if (chan->int_.auto_ && chan->int_.clocks == 0) { - chan->int_.enb = 0; + /* Envelope modification */ + if (chan->env.enb && chan->env.clocks == 0) { + if (chan->env.dir == 0 && chan->env.value != 0) + chan->env.value--; + else if (chan->env.dir == 1 && chan->env.value != 15) + chan->env.value++; + else if (chan->env.rep) + chan->env.value = chan->env.reload; + chan->env.clocks = ((uint32_t) chan->env.interval + 1) * 307220; + } + + /* Frequency modification */ + if (chan->freqmod && sim->vsu.freqmod.clocks == 0) { + chan->freq.current = sim->vsu.freqmod.next; + vsuNextFreqMod(sim, chan); + if (!chan->int_.enb) return; - } - - /* Next sample */ - if (chan->clocks == 0) { - - /* Wave */ - if (index != 5) { - chan->clocks = 4 * (2048 - (uint32_t) chan->freq.current); - chan->wave.sample = (chan->wave.sample + 1) & 31; - } - - /* Noise */ - else { - chan->clocks = 40 * (2048 - (uint32_t) chan->freq.current); - bit = (( - sim->vsu.noise.register_ >> NOISE_TAPS[sim->vsu.noise.tap]^ - sim->vsu.noise.register_ >> 7 - ) & 1) ^ 1; - sim->vsu.noise.register_ = bit | - (sim->vsu.noise.register_ << 1 & 0x7FFE); - } - - } - - /* Envelope modification */ - if (chan->env.enb && chan->env.clocks == 0) { - if (chan->env.dir == 0 && chan->env.value != 0) - chan->env.value--; - else if (chan->env.dir == 1 && chan->env.value != 15) - chan->env.value++; - else if (chan->env.rep) - chan->env.value = chan->env.reload; - chan->env.clocks = ((uint32_t) chan->env.interval + 1) * 307220; - } - - /* Frequency modification */ - if (freqmod && sim->vsu.freqmod.clocks == 0) { - chan->freq.current = sim->vsu.freqmod.next; - vsuNextFreqMod(sim, chan); - if (!chan->int_.enb) - return; - sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval * - (sim->vsu.freqmod.clk == 0 ? 19200 : 153600); - } - - } while (clocks != 0); + sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval * + (sim->vsu.freqmod.clk == 0 ? 19200 : 153600); + } } @@ -230,6 +187,8 @@ static void vsuWriteEV1(VB *sim, int index, uint8_t value) { sim->vsu.freqmod.func = value >> 4 & 1; sim->vsu.freqmod.rep = value >> 5 & 1; vsuNextFreqMod(sim, chan); + chan->freqmod = + sim->vsu.freqmod.enb && sim->vsu.freqmod.interval != 0; break; case 5: /* Channel 6 */ @@ -313,6 +272,8 @@ static void vsuWriteSWP(VB *sim, uint8_t value) { (sim->vsu.freqmod.clk == 0 ? 19200 : 153600); if (clocks < sim->vsu.freqmod.clocks) sim->vsu.freqmod.clocks = clocks; + sim->vsu.channels[4].freqmod = + sim->vsu.freqmod.enb && sim->vsu.freqmod.interval != 0; } @@ -321,6 +282,8 @@ static void vsuWriteSWP(VB *sim, uint8_t value) { /* Process component */ static void vsuEmulate(VB *sim, uint32_t clocks) { + Channel *chan; /* Input channel */ + uint32_t chantil; /* Clocks until next channel state update */ float i0; /* Current analog input sample */ float o0; /* Current analog output sample */ uint16_t output[2]; /* Digital output samples */ @@ -340,8 +303,50 @@ static void vsuEmulate(VB *sim, uint32_t clocks) { sim->vsu.clocks -= until; /* Process all channels */ - for (x = 0; x < 6; x++) - vsuEmulateChannel(sim, x, until); + for (x = 0; x < 6; x++) { + chan = &sim->vsu.channels[x]; + chantil = until; + + /* Process all clocks */ + while (chan->int_.enb && chantil != 0) { + + /* Determine when the next state change will occur */ + if (chan->until == 0) { + + /* Clocks until next state change */ + chan->until = chan->clocks; + if (chan->env.enb && chan->env.clocks < chan->until) + chan->until = chan->env.clocks; + if (chan->int_.auto_ && chan->int_.clocks < chan->until) + chan->until = chan->int_.clocks; + if (chan->freqmod && sim->vsu.freqmod.clocks < chan->until) + chan->until = sim->vsu.freqmod.clocks; + + /* Manage clocks */ + chan->clocks -= chan->until; + if (chan->env.enb) + chan->env.clocks -= chan->until; + if (chan->int_.auto_) + chan->int_.clocks -= chan->until; + if (chan->freqmod) + sim->vsu.freqmod.clocks -= chan->until; + } + + /* Manage clocks */ + if (chan->until > chantil) { + chan->until -= chantil; + chantil = 0; + } else { + chantil -= chan->until; + chan->until = 0; + } + + /* Update channel state */ + if (chan->until == 0) + vsuEmulateChannel(sim, chan, chan->until); + } + + } /* Wait for the current sample to finish */ if (sim->vsu.clocks != 0) @@ -416,6 +421,8 @@ static void vsuReset(VB *sim) { for (x = 0; x < 6; x++) { chan = &sim->vsu.channels[x]; chan->clocks = 0; + chan->freqmod = 0; + chan->until = 0; chan->env.clocks = 0; chan->env.enb = 0; chan->env.dir = 0;