shrooms-vb-core/core/vsu.c

569 lines
19 KiB
C

/* This file is included into vb.c and cannot be compiled on its own. */
#ifdef VBAPI
/********************************* Constants *********************************/
/* Analog output high-pass filter coefficient = 0.022 / (0.022 + 1 / 41700) */
#define RC_A (float) (1 - 1 / 918.4)
/******************************** Lookup Data ********************************/
/* Noise feedback bit positions by tap field */
static uint8_t NOISE_TAPS[] = { 14, 10, 13, 4, 8, 6, 9, 11 };
/* Clock flags per output sample -- 479 clocks plus bit value */
/* Ensures exactly 200,000 clocks every 417 samples = 41,700 Hz */
static uint32_t SAMPLE_CLOCKS[] = {
0xD6B6B5B5, 0x5ADAD6D6, 0x6D6B6B5B, 0xB5B5ADAD,
0xD6D6B6B5, 0x6B5B5ADA, 0xAD6D6D6B, 0xB6B5B5AD,
0x5ADAD6D6, 0x6B6B5B5B, 0xB5ADAD6D, 0xD6D6B6B5,
0x5B5ADAD6, 1
};
/***************************** Callback Handlers *****************************/
/* Samples buffer is full */
#ifndef VB_DIRECT_SAMPLES
#define VB_ON_SAMPLES sim->onSamples
#else
extern void VB_DIRECT_SAMPLES(VB *, void *, uint32_t);
#define VB_ON_SAMPLES VB_DIRECT_SAMPLES
#endif
static void vsuOnSamples(VB *sim) {
if (sim->onSamples != NULL)
VB_ON_SAMPLES(sim, sim->vsu.out.samples, sim->vsu.out.capacity);
}
#undef VB_ON_SAMPLES
/***************************** Module Functions ******************************/
/* Stage the next frequency modification value */
static void vsuNextFreqMod(VB *sim, Channel *chan) {
uint16_t next;
/* Sweep */
if (sim->vsu.freqmod.func == 0) {
next = chan->freq.current >> sim->vsu.freqmod.shift;
if (sim->vsu.freqmod.dir == 0)
next = chan->freq.current - next;
else next = chan->freq.current + next;
if (next > 2047)
chan->int_.enb = 0;
}
/* Modulation */
else {
next = (chan->freq.written +
sim->vsu.modulation[sim->vsu.freqmod.sample]) & 2047;
if (sim->vsu.freqmod.sample != 31)
sim->vsu.freqmod.sample++;
else if (sim->vsu.freqmod.rep)
sim->vsu.freqmod.sample = 0;
}
/* Configure state */
sim->vsu.freqmod.next = next;
}
/* Process one channel */
static void vsuEmulateChannel(VB *sim, Channel *chan, uint32_t clocks) {
uint32_t bit; /* Pseudorandom bit */
/* Automatic shutoff */
if (chan->int_.auto_ && chan->int_.clocks == 0) {
chan->int_.enb = 0;
return;
}
/* Next input sample */
if (chan->clocks == 0) {
/* Wave */
if (chan != &sim->vsu.channels[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 (chan->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);
}
}
/* Produce output for a channel */
static void vsuOutputChannel(VB *sim, int index, uint16_t *output) {
Channel *chan; /* Channel handle */
uint32_t level; /* Stereo output level */
uint32_t sample; /* Input sample */
int e; /* Iterator */
/* Select channel */
chan = &sim->vsu.channels[index];
/* Select input sample */
sample =
!chan->int_.enb ? 0 : /* Disabled */
index == 5 ? (sim->vsu.noise.register_ & 1) ? 63 : 0 : /* Noise */
chan->wave.wave > 4 ? 0 : /* Wave range */
sim->vsu.waves[chan->wave.wave][chan->wave.sample] /* Wave */
;
/* Compute output samples */
for (e = 0; e < 2; e++) {
level = e == 0 ? chan->lrv.left : chan->lrv.right;
level = (level * chan->env.value >> 3) + (level && chan->env.value);
output[e] += level * sample;
}
}
/* Write a value to S*EV0 */
static void vsuWriteEV0(VB *sim, int index, uint8_t value) {
Channel *chan = &sim->vsu.channels[index];
/* Parse fields */
chan->env.dir = value >> 3 & 1;
chan->env.interval = value & 7;
chan->env.value = value >> 4 & 15;
chan->env.reload = chan->env.value;
if (index == 4)
vsuNextFreqMod(sim, chan);
/* Configure state */
chan->env.clocks = 307220 * ((uint32_t) chan->env.interval + 1);
}
/* Write a value to S*EV1 */
static void vsuWriteEV1(VB *sim, int index, uint8_t value) {
Channel *chan = &sim->vsu.channels[index];
/* Parse fields */
chan->env.enb = value & 1;
chan->env.rep = value >> 1 & 1;
/* Processing by channel */
switch (index) {
case 4: /* Channel 5 */
sim->vsu.freqmod.enb = value >> 6 & 1;
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 */
sim->vsu.noise.tap = value >> 4 & 7;
sim->vsu.noise.register_ = 0x0000;
}
}
/* Write a value to S*FQH */
static void vsuWriteFQH(VB *sim, int index, uint16_t value) {
Channel *chan = &sim->vsu.channels[index];
value = value << 8 & 0x0700;
chan->freq.current = (chan->freq.current & 0x00FF) | value;
chan->freq.written = (chan->freq.written & 0x00FF) | value;
if (index == 4)
vsuNextFreqMod(sim, chan);
}
/* Write a value to S*FQL */
static void vsuWriteFQL(VB *sim, int index, uint8_t value) {
Channel *chan = &sim->vsu.channels[index];
chan->freq.current = (chan->freq.current & 0x0700) | value;
chan->freq.written = (chan->freq.written & 0x0700) | value;
if (index == 4)
vsuNextFreqMod(sim, chan);
}
/* Write a value to S*INT */
static void vsuWriteINT(VB *sim, int index, uint8_t value) {
Channel *chan = &sim->vsu.channels[index];
/* Parse fields */
chan->int_.auto_ = value >> 5 & 1;
chan->int_.enb = value >> 7 & 1;
chan->int_.interval = value & 31;
/* Update state */
chan->int_.clocks = 76805 * ((uint32_t) chan->int_.interval + 1);
if (chan->env.enb)
chan->env.clocks = 307220 * ((uint32_t) chan->env.interval + 1);
if (index != 5) {
chan->wave.sample = 0;
chan->clocks = 4 * (2048 - (uint32_t) chan->freq.current);
} else {
sim->vsu.noise.register_ = 0x0000;
chan->clocks = 40 * (2048 - (uint32_t) chan->freq.current);
}
if (index == 4) {
sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval *
(sim->vsu.freqmod.clk == 0 ? 19200 : 153600);
sim->vsu.freqmod.sample = 0;
}
}
/* Write a value to S*LRV */
static void vsuWriteLRV(VB *sim, int index, uint8_t value) {
Channel *chan = &sim->vsu.channels[index];
chan->lrv.left = value >> 4 & 15;
chan->lrv.right = value & 15;
}
/* Write a value to S*RAM */
static void vsuWriteRAM(VB *sim, int index, uint8_t value) {
sim->vsu.channels[index].wave.wave = value & 7;
}
/* Write a value to S5SWP */
static void vsuWriteSWP(VB *sim, uint8_t value) {
uint32_t clocks;
/* Parse fields */
sim->vsu.freqmod.clk = value >> 7 & 1;
sim->vsu.freqmod.dir = value >> 3 & 1;
sim->vsu.freqmod.interval = value >> 4 & 7;
sim->vsu.freqmod.shift = value & 7;
/* Configure state */
clocks = (uint32_t) sim->vsu.freqmod.interval *
(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;
}
/***************************** Module Functions ******************************/
/* 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 */
uint32_t until; /* Clocks to process channels */
int e, x; /* Iterators */
/* Process all clocks */
do {
/* Clocks until next sample */
until = clocks;
if (sim->vsu.clocks < until)
until = sim->vsu.clocks;
/* Manage clocks */
clocks -= until;
sim->vsu.clocks -= until;
/* Process all channels */
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)
return;
/* Compute the output sample */
output[0] = output[1] = 0;
for (x = 0; x < 6; x++)
vsuOutputChannel(sim, x, output);
/* Output sample */
for (e = 0; e < 2; e++) {
/* Compute the output sample for this stereo channel */
i0 = (output[e] >> 4) / 685.0f;
o0 = RC_A * (sim->vsu.out.o1[e] + i0 - sim->vsu.out.i1[e]);
if (o0 < -1.0f) o0 = -1.0f;
if (o0 > +1.0f) o0 = +1.0f;
sim->vsu.out.i1[e] = i0;
sim->vsu.out.o1[e] = o0;
/* Output the sample to caller memory */
if (
sim->vsu.out.samples != NULL &&
sim->vsu.out.offset >> 1 < sim->vsu.out.capacity
) {
/* Processing by data type */
switch (sim->vsu.out.type) {
case VB_S16:
((int16_t *) sim->vsu.out.samples)
[sim->vsu.out.offset++] = (int16_t)
(o0 * 32767.0f + (o0 < 0.0f ? -0.5f : +0.5f));
break;
case VB_F32:
((float *) sim->vsu.out.samples)
[sim->vsu.out.offset++] = o0;
break;
}
/* Invoke the samples callback */
if (
e == 1 &&
sim->vsu.out.offset >> 1 == sim->vsu.out.capacity
) vsuOnSamples(sim);
}
}
/* Advance to the next sample */
sim->vsu.sample += sim->vsu.sample == 416 ? -416 : 1;
sim->vsu.clocks = 479 + (1 &
SAMPLE_CLOCKS[sim->vsu.sample >> 5] >> (sim->vsu.sample & 31));
} while (clocks != 0);
}
/* Simulate a hardware reset */
static void vsuReset(VB *sim) {
Channel *chan; /* Channel handle */
int x, y; /* Iterators */
/* Memory */
for (x = 0; x < 32; x++) {
sim->vsu.modulation[x] = 0;
for (y = 0; y < 5; y++)
sim->vsu.waves[y][x] = 0;
}
/* Channel state */
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;
chan->env.interval = 0;
chan->env.reload = 0;
chan->env.rep = 0;
chan->env.value = 0;
chan->freq.current = 0x000;
chan->freq.written = 0x000;
chan->lrv.left = 0;
chan->lrv.right = 0;
chan->int_.auto_ = 0;
chan->int_.clocks = 0;
chan->int_.enb = 0;
chan->int_.interval = 0;
chan->wave.sample = 0;
chan->wave.wave = 0;
}
/* Other channel state */
sim->vsu.freqmod.clk = 0;
sim->vsu.freqmod.clocks = 0;
sim->vsu.freqmod.dir = 0;
sim->vsu.freqmod.enb = 0;
sim->vsu.freqmod.func = 0;
sim->vsu.freqmod.interval = 0;
sim->vsu.freqmod.next = 0;
sim->vsu.freqmod.rep = 0;
sim->vsu.freqmod.sample = 0;
sim->vsu.freqmod.shift = 0;
sim->vsu.noise.register_ = 0;
sim->vsu.noise.tap = 0;
/* Other */
sim->vsu.clocks = 479 + (SAMPLE_CLOCKS[0] & 1);
sim->vsu.sample = 0;
for (x = 0; x < 2; x++)
sim->vsu.out.i1[x] = sim->vsu.out.o1[x] = 0;
}
/* Write a typed value to the VSU bus */
static void vsuWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){
/* Only byte writes are allowed */
switch (type) {
case VB_S16:
case VB_U16:
case VB_S32:
return;
}
/* Unmapped */
if (address & 3)
return;
/* Working variables */
address &= 0x7FF;
/* Wave memory */
if (address < 0x280) {
if (debug || !(
sim->vsu.channels[0].int_.enb |
sim->vsu.channels[1].int_.enb |
sim->vsu.channels[2].int_.enb |
sim->vsu.channels[3].int_.enb |
sim->vsu.channels[4].int_.enb |
sim->vsu.channels[5].int_.enb
)) sim->vsu.waves[address >> 7][address >> 2 & 31] = value & 63;
}
/* Modulation memory */
else if (address < 0x300) {
if (debug || !sim->vsu.channels[4].int_.enb)
sim->vsu.modulation[address >> 2 & 31] = value;
}
/* I/O register */
else switch (address >> 2) {
case 0x400>>2: vsuWriteINT(sim, 0, value); break; /* S1INT */
case 0x404>>2: vsuWriteLRV(sim, 0, value); break; /* S1LRV */
case 0x408>>2: vsuWriteFQL(sim, 0, value); break; /* S1FQL */
case 0x40C>>2: vsuWriteFQH(sim, 0, value); break; /* S1FQH */
case 0x410>>2: vsuWriteEV0(sim, 0, value); break; /* S1EV0 */
case 0x414>>2: vsuWriteEV1(sim, 0, value); break; /* S1EV1 */
case 0x418>>2: vsuWriteRAM(sim, 0, value); break; /* S1RAM */
case 0x440>>2: vsuWriteINT(sim, 1, value); break; /* S2INT */
case 0x444>>2: vsuWriteLRV(sim, 1, value); break; /* S2LRV */
case 0x448>>2: vsuWriteFQL(sim, 1, value); break; /* S2FQL */
case 0x44C>>2: vsuWriteFQH(sim, 1, value); break; /* S2FQH */
case 0x450>>2: vsuWriteEV0(sim, 1, value); break; /* S2EV0 */
case 0x454>>2: vsuWriteEV1(sim, 1, value); break; /* S2EV1 */
case 0x458>>2: vsuWriteRAM(sim, 1, value); break; /* S2RAM */
case 0x480>>2: vsuWriteINT(sim, 2, value); break; /* S3INT */
case 0x484>>2: vsuWriteLRV(sim, 2, value); break; /* S3LRV */
case 0x488>>2: vsuWriteFQL(sim, 2, value); break; /* S3FQL */
case 0x48C>>2: vsuWriteFQH(sim, 2, value); break; /* S3FQH */
case 0x490>>2: vsuWriteEV0(sim, 2, value); break; /* S3EV0 */
case 0x494>>2: vsuWriteEV1(sim, 2, value); break; /* S3EV1 */
case 0x498>>2: vsuWriteRAM(sim, 2, value); break; /* S3RAM */
case 0x4C0>>2: vsuWriteINT(sim, 3, value); break; /* S4INT */
case 0x4C4>>2: vsuWriteLRV(sim, 3, value); break; /* S4LRV */
case 0x4C8>>2: vsuWriteFQL(sim, 3, value); break; /* S4FQL */
case 0x4CC>>2: vsuWriteFQH(sim, 3, value); break; /* S4FQH */
case 0x4D0>>2: vsuWriteEV0(sim, 3, value); break; /* S4EV0 */
case 0x4D4>>2: vsuWriteEV1(sim, 3, value); break; /* S4EV1 */
case 0x4D8>>2: vsuWriteRAM(sim, 3, value); break; /* S4RAM */
case 0x500>>2: vsuWriteINT(sim, 4, value); break; /* S5INT */
case 0x504>>2: vsuWriteLRV(sim, 4, value); break; /* S5LRV */
case 0x508>>2: vsuWriteFQL(sim, 4, value); break; /* S5FQL */
case 0x50C>>2: vsuWriteFQH(sim, 4, value); break; /* S5FQH */
case 0x510>>2: vsuWriteEV0(sim, 4, value); break; /* S5EV0 */
case 0x514>>2: vsuWriteEV1(sim, 4, value); break; /* S5EV1 */
case 0x518>>2: vsuWriteRAM(sim, 4, value); break; /* S5RAM */
case 0x51C>>2: vsuWriteSWP(sim, value); break; /* S5SWP */
case 0x540>>2: vsuWriteINT(sim, 5, value); break; /* S6INT */
case 0x544>>2: vsuWriteLRV(sim, 5, value); break; /* S6LRV */
case 0x548>>2: vsuWriteFQL(sim, 5, value); break; /* S6FQL */
case 0x54C>>2: vsuWriteFQH(sim, 5, value); break; /* S6FQH */
case 0x550>>2: vsuWriteEV0(sim, 5, value); break; /* S6EV0 */
case 0x554>>2: vsuWriteEV1(sim, 5, value); break; /* S6EV1 */
case 0x580>>2: /* SSTOP */
if ((value & 1) == 0)
break;
sim->vsu.channels[0].int_.enb =
sim->vsu.channels[1].int_.enb =
sim->vsu.channels[2].int_.enb =
sim->vsu.channels[3].int_.enb =
sim->vsu.channels[4].int_.enb =
sim->vsu.channels[5].int_.enb = 0;
}
}
#endif /* VBAPI */