From 3afe0282c2b4e72214c9442b325deb8caf21b0d4 Mon Sep 17 00:00:00 2001 From: Guy Perfect Date: Sun, 20 Oct 2024 18:52:19 -0500 Subject: [PATCH] Implement VSU --- core/bus.c | 7 +- core/cpu.c | 10 +- core/game-pad.c | 7 +- core/timer.c | 6 +- core/vb.c | 199 +++++++++++++++--- core/vb.h | 18 +- core/vsu.c | 530 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 734 insertions(+), 43 deletions(-) create mode 100644 core/vsu.c diff --git a/core/bus.c b/core/bus.c index 1017613..8804b64 100644 --- a/core/bus.c +++ b/core/bus.c @@ -3,7 +3,7 @@ -/********************************* Constants *********************************/ +/******************************** Lookup Data ********************************/ /* Memory access address masks by data type */ static const uint32_t TYPE_MASKS[] = { @@ -20,6 +20,7 @@ static const uint32_t TYPE_MASKS[] = { static void vipRead (VB *, uint32_t, int, int32_t *); static void vipWrite(VB *, uint32_t, int, int32_t, int); +static void vsuWrite(VB *, uint32_t, int, int32_t, int); @@ -197,7 +198,9 @@ static void busWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){ vipWrite(sim, address, type, value, debug); break; - case 1: break; /* VSU */ + case 1: /* VSU */ + vsuWrite(sim, address, type, value, debug); + break; case 2: /* Misc. I/O */ busWriteMisc(sim, address, type, value); diff --git a/core/cpu.c b/core/cpu.c index fcdb3c0..11be367 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -857,7 +857,7 @@ static int cpuFloatCommon(VB *sim, double value, int32_t *bits) { } /* Underflow */ - if (value < 0 ? value > -FLT_MIN : value < FLT_MIN) { + if (value != 0 && value > -FLT_MIN && value < FLT_MIN) { sim->cpu.psw.fud = 1; floatVal = 0; /* TODO: Can this produce negative zero? */ @@ -872,10 +872,10 @@ static int cpuFloatCommon(VB *sim, double value, int32_t *bits) { /* Update state */ *bits = cpuFloatToWord(floatVal); - sim->cpu.psw.cy = floatVal < 0; - sim->cpu.psw.ov = 0; - sim->cpu.psw.s = floatVal < 0; - sim->cpu.psw.z = floatVal == 0; + sim->cpu.psw.cy = floatVal < 0; + sim->cpu.psw.ov = 0; + sim->cpu.psw.s = floatVal < 0; + sim->cpu.psw.z = floatVal == 0; return 0; } diff --git a/core/game-pad.c b/core/game-pad.c index f0b5067..70ad9a8 100644 --- a/core/game-pad.c +++ b/core/game-pad.c @@ -6,16 +6,16 @@ /***************************** Library Functions *****************************/ /* Process component */ -static int padEmulate(VB *sim, uint32_t clocks) { +static void padEmulate(VB *sim, uint32_t clocks) { /* No hardware read in progress */ if (sim->pad.si_stat == 0) - return 0; + return; /* Hardware read completes after time to process */ if (sim->pad.si_stat > clocks) { sim->pad.si_stat -= clocks; - return 0; + return; } /* Complete hardware read */ @@ -30,7 +30,6 @@ static int padEmulate(VB *sim, uint32_t clocks) { (sim->pad.keys & 0x000E) == 0 ) sim->cpu.irq |= 0x0001; - return 0; } /* Read a value from SCR */ diff --git a/core/timer.c b/core/timer.c index a949b6b..94eb5cf 100644 --- a/core/timer.c +++ b/core/timer.c @@ -21,11 +21,11 @@ static void tmrUpdate(VB *sim, uint16_t value) { /***************************** Library Functions *****************************/ /* Process component */ -static int tmrEmulate(VB *sim, uint32_t clocks) { +static void tmrEmulate(VB *sim, uint32_t clocks) { /* Timer is disabled */ if (!sim->tmr.t_enb) - return 0; + return; /* Process all clocks */ for (;;) { @@ -34,7 +34,7 @@ static int tmrEmulate(VB *sim, uint32_t clocks) { if (sim->tmr.clocks > clocks) { sim->tmr.clocks -= clocks; sim->tmr.until -= clocks; - return 0; + return; } /* Advance forward the component's number of clocks */ diff --git a/core/vb.c b/core/vb.c index 1a0221a..242e66e 100644 --- a/core/vb.c +++ b/core/vb.c @@ -11,6 +11,63 @@ /* Output image */ typedef uint8_t Pixels[2][384*224]; +/* VSU channel */ +typedef struct { + + /* Envelope */ + struct { + + /* Register state */ + uint8_t enb; /* Modifications enabled */ + uint8_t dir; /* Modification direction */ + uint8_t interval; /* Modification interval */ + uint8_t rep; /* Repeat modifications */ + uint8_t value; /* Master output level */ + + /* Other state */ + uint32_t clocks; /* Clocks until modification */ + uint8_t reload; /* Automatic reload value */ + } env; + + /* Frequency */ + struct { + uint16_t current; /* Current value */ + uint16_t written; /* Last value written */ + } freq; + + /* Stereo levels */ + struct { + uint8_t left; /* Left output level */ + uint8_t right; /* Right output level */ + } lrv; + + /* Control */ + struct { + + /* Register state */ + uint8_t auto_; /* Shutoff enabled */ + uint8_t enb; /* Sound generation enabled */ + uint8_t interval; /* Shutoff interval */ + + /* Other state */ + uint32_t clocks; /* Clocks until shutoff */ + } int_; + + /* Waveform */ + struct { + + /* Register state */ + uint8_t wave; /* Waveform index */ + + /* Other state */ + int sample; /* Current sample index */ + } wave; + + /* Other state */ + uint32_t clocks; /* Clocks until next sample */ + uint16_t output[2]; /* Current output sample */ +} Channel; + /* Simulation state */ struct VB { @@ -93,7 +150,7 @@ struct VB { /* Game pad */ struct { - /* Hardware state */ + /* Register state */ uint8_t k_int_inh; /* Interrupt acknowledge/disable */ uint8_t para_si; /* Read reset signal */ uint8_t s_abt_dis; /* Abort hardware read */ @@ -102,7 +159,7 @@ struct VB { uint32_t si_stat; /* Hardware read in progress */ uint8_t soft_ck; /* Controller communication signal */ - /* Simulation state */ + /* Other state */ uint16_t keys; /* Next input bits */ int step; /* Software read processing phase */ } pad; @@ -110,13 +167,13 @@ struct VB { /* Timer */ struct { - /* Hardware state */ + /* Register state */ uint8_t t_clk_sel; /* Counter tick duration */ uint8_t t_enb; /* Enable timer */ uint8_t tim_z_int; /* Enable interrupt */ uint8_t z_stat; /* Zero status */ - /* Simulation state */ + /* Other state */ uint32_t clocks; /* Master clocks to wait */ uint16_t counter; /* Current counter value */ uint16_t reload; /* Reload counter value */ @@ -135,7 +192,7 @@ struct VB { /* Display processor */ struct { - /* Hardware state */ + /* Register state */ uint8_t disp; /* Display enabled */ uint8_t fclk; /* Frame clock signal high */ uint8_t l0bsy; /* Displaying left frame buffer 0 */ @@ -147,7 +204,7 @@ struct VB { uint8_t scanrdy; /* Mirrors are stable */ uint8_t synce; /* Servo enabled */ - /* Simulation state */ + /* Other state */ uint8_t brt[4]; /* Precomputed brightness */ int buffer; /* Index of frame buffer to display */ uint32_t clocks; /* Master clocks to wait */ @@ -163,7 +220,7 @@ struct VB { /* Pixel processor */ struct { - /* Hardware state */ + /* Register state */ uint8_t f0bsy; /* Drawing into frame buffer 0 */ uint8_t f1bsy; /* Drawing into frame buffer 1 */ uint8_t overtime; /* Drawing extends into display interval */ @@ -172,7 +229,7 @@ struct VB { uint32_t sbout; /* Drawing specified vertical output position */ uint8_t xpen; /* Drawing enabled */ - /* Simulation state */ + /* Other state */ uint32_t clocks; /* Master clocks to wait */ int column; /* Current horizontal output position */ int frame; /* FRMCYC counter */ @@ -200,6 +257,58 @@ struct VB { uint8_t ram[0x40000]; /* Video memory */ } vip; + /* VSU */ + struct { + + /* Audio sources */ + Channel channels[6]; + + /* Channel 5 frequency modification */ + struct { + + /* Register state */ + uint8_t clk; /* Base modification clock */ + uint8_t dir; /* Sweep direction */ + uint8_t enb; /* Modifications enabled */ + uint8_t func; /* Modification function */ + uint8_t interval; /* Modification interval */ + uint8_t rep; /* Repeat modulation */ + uint8_t shift; /* Sweep shift amount */ + + /* Other state */ + uint32_t clocks; /* Clocks until modification */ + uint16_t next; /* Next frequency value */ + int sample; /* Current sample index */ + } freqmod; + + /* Channel 6 noise generator */ + struct { + + /* Register state */ + uint8_t tap; /* LSFR feedback bit position */ + + /* Other state */ + uint16_t register_; /* Pseudorandom bits */ + } noise; + + /* Sample output */ + struct { + float i1[2]; /* Previous analog input sample */ + float o1[2]; /* Previous analog output sample */ + uint32_t capacity; /* Number of audio frames in samples */ + uint32_t offset; /* Position in output buffer */ + int16_t *samples; /* Output memory */ + } out; + + /* Memory */ + int8_t modulation[32]; /* Modulation amounts */ + uint8_t waves[5][32]; /* Wafeform samples */ + + /* Other state */ + uint32_t clocks; /* Clocks until next output sample */ + int sample; /* Output sample index, period 417 */ + } vsu; + /* Other state */ uint8_t wcr; /* Wait controller state */ uint8_t wram[0x10000]; /* System RAM */ @@ -210,6 +319,7 @@ struct VB { vbOnFetch onFetch; /* CPU instruction fetch */ vbOnFrame onFrame; /* VIP frame ready */ vbOnRead onRead; /* CPU instruction read */ + vbOnSamples onSamples; /* VSU samples full */ vbOnWrite onWrite; /* CPU instruction write */ void *tag; /* User data */ }; @@ -238,6 +348,7 @@ static int32_t SignExtend(int32_t value, int32_t bits) { #include "bus.c" #include "cpu.c" #include "vip.c" +#include "vsu.c" @@ -245,20 +356,21 @@ static int32_t SignExtend(int32_t value, int32_t bits) { /* Process a simulation for a given number of clocks */ static int sysEmulate(VB *sim, uint32_t clocks) { - return - cpuEmulate(sim, clocks) | - vipEmulate(sim, clocks) | - padEmulate(sim, clocks) | - tmrEmulate(sim, clocks) - ; + int ret; + ret = cpuEmulate(sim, clocks); + padEmulate(sim, clocks); + tmrEmulate(sim, clocks); + vsuEmulate(sim, clocks); + ret |= vipEmulate(sim, clocks); + return ret; } /* Determine how many clocks are guaranteed to process */ static uint32_t sysUntil(VB *sim, uint32_t clocks) { clocks = cpuUntil(sim, clocks); - clocks = vipUntil(sim, clocks); clocks = padUntil(sim, clocks); clocks = tmrUntil(sim, clocks); + clocks = vipUntil(sim, clocks); return clocks; } @@ -395,6 +507,27 @@ VBAPI vbOnRead vbGetReadCallback(VB *sim) { return sim->onRead; } +/* Retrieve the audio samples buffer */ +VBAPI void* vbGetSamples(VB *sim, uint32_t *capacity, uint32_t *position) { + if (capacity == NULL) { + if (capacity != NULL) + *capacity = 0; + if (position != NULL) + *position = 0; + } else { + if (capacity != NULL) + *capacity = sim->vsu.out.capacity; + if (position != NULL) + *position = sim->vsu.out.offset >> 1; + } + return sim->vsu.out.samples; +} + +/* Retrieve the samples callback handler */ +VBAPI vbOnSamples vbGetSamplesCallback(VB *sim) { + return sim->onSamples; +} + /* Retrieve the value in a system register */ VBAPI uint32_t vbGetSystemRegister(VB *sim, int index) { return index < 0 || index > 31 ? 0 : cpuGetSystemRegister(sim, index); @@ -412,13 +545,15 @@ VBAPI vbOnWrite vbGetWriteCallback(VB *sim) { /* Initialize a simulation instance */ VBAPI VB* vbInit(VB *sim) { - sim->cart.ram = NULL; - sim->cart.rom = NULL; - sim->onExecute = NULL; - sim->onFetch = NULL; - sim->onFrame = NULL; - sim->onRead = NULL; - sim->onWrite = NULL; + sim->cart.ram = NULL; + sim->cart.rom = NULL; + sim->vsu.out.samples = NULL; + sim->onExecute = NULL; + sim->onFetch = NULL; + sim->onFrame = NULL; + sim->onRead = NULL; + sim->onSamples = NULL; + sim->onWrite = NULL; vbReset(sim); return sim; } @@ -445,9 +580,10 @@ VBAPI VB* vbReset(VB *sim) { /* Components */ cpuReset(sim); - vipReset(sim); padReset(sim); tmrReset(sim); + vipReset(sim); + vsuReset(sim); return sim; } @@ -526,6 +662,23 @@ VBAPI vbOnRead vbSetReadCallback(VB *sim, vbOnRead callback) { return prev; } +/* Specify a new audio samples buffer */ +VBAPI int vbSetSamples(VB *sim, void *samples, uint32_t capacity) { + if (capacity == 0 || capacity > 0x40000000) + return 1; + sim->vsu.out.capacity = capacity; + sim->vsu.out.offset = 0; + sim->vsu.out.samples = samples; + return 0; +} + +/* Specify a new samples callback handler */ +VBAPI vbOnSamples vbSetSamplesCallback(VB *sim, vbOnSamples callback) { + vbOnSamples prev = sim->onSamples; + sim->onSamples = callback; + return prev; +} + /* Specify a new value for a system register */ VBAPI uint32_t vbSetSystemRegister(VB *sim, int index, uint32_t value) { return index < 0 || index > 31 ? 0 : diff --git a/core/vb.h b/core/vb.h index 667f4f7..0266ab5 100644 --- a/core/vb.h +++ b/core/vb.h @@ -23,6 +23,7 @@ extern "C" { VB_DIRECT_FETCH VB_DIRECT_FRAME VB_DIRECT_READ + VB_DIRECT_SAMPLES VB_DIRECT_WRITE Implements callbacks as direct function calls with names given by the above @@ -86,12 +87,13 @@ extern "C" { typedef struct VB VB; /* Callbacks */ -typedef int (*vbOnException)(VB *sim, uint16_t *cause); -typedef int (*vbOnExecute )(VB *sim, uint32_t address, const uint16_t *code, int length); -typedef int (*vbOnFetch )(VB *sim, int fetch, uint32_t address, int32_t *value, uint32_t *cycles); -typedef int (*vbOnFrame )(VB *sim); -typedef int (*vbOnRead )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles); -typedef int (*vbOnWrite )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles, int *cancel); +typedef int (*vbOnException)(VB *sim, uint16_t *cause); +typedef int (*vbOnExecute )(VB *sim, uint32_t address, const uint16_t *code, int length); +typedef int (*vbOnFetch )(VB *sim, int fetch, uint32_t address, int32_t *value, uint32_t *cycles); +typedef int (*vbOnFrame )(VB *sim); +typedef int (*vbOnRead )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles); +typedef void (*vbOnSamples )(VB *sim, int16_t *buffer, uint32_t capacity); +typedef int (*vbOnWrite )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles, int *cancel); @@ -112,6 +114,8 @@ VBAPI void vbGetPixels (VB *sim, void *left, int leftStrideX, VBAPI uint32_t vbGetProgramCounter (VB *sim); VBAPI int32_t vbGetProgramRegister (VB *sim, int index); VBAPI vbOnRead vbGetReadCallback (VB *sim); +VBAPI void* vbGetSamples (VB *sim, uint32_t *capacity, uint32_t *position); +VBAPI vbOnSamples vbGetSamplesCallback (VB *sim); VBAPI uint32_t vbGetSystemRegister (VB *sim, int index); VBAPI void* vbGetUserData (VB *sim); VBAPI vbOnWrite vbGetWriteCallback (VB *sim); @@ -128,6 +132,8 @@ VBAPI uint16_t vbSetKeys (VB *sim, uint16_t keys); VBAPI uint32_t vbSetProgramCounter (VB *sim, uint32_t value); VBAPI int32_t vbSetProgramRegister (VB *sim, int index, int32_t value); VBAPI vbOnRead vbSetReadCallback (VB *sim, vbOnRead callback); +VBAPI int vbSetSamples (VB *sim, void *samples, uint32_t capacity); +VBAPI vbOnSamples vbSetSamplesCallback (VB *sim, vbOnSamples callback); VBAPI uint32_t vbSetSystemRegister (VB *sim, int index, uint32_t value); VBAPI void* vbSetUserData (VB *sim, void *tag); VBAPI vbOnWrite vbSetWriteCallback (VB *sim, vbOnWrite callback); diff --git a/core/vsu.c b/core/vsu.c new file mode 100644 index 0000000..db5fc14 --- /dev/null +++ b/core/vsu.c @@ -0,0 +1,530 @@ +/* This file is included into vb.c and cannot be compiled on its own. */ +#ifdef VBAPI + + + +/********************************* Constants *********************************/ + +/* Analog output low-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_EXCEPTION + + + +/***************************** 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, int index, uint32_t clocks) { + uint32_t bit; /* Pseudorandom bit */ + Channel *chan; /* Channel handle */ + uint32_t level; /* Stereo output level */ + uint32_t sample; /* Input sample */ + uint32_t until; /* Clocks to process sub-channel components */ + 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); + chan->output[e] = level * sample; + } + + /* Channel is disabled */ + if (!chan->int_.enb) + return; + + /* Process all clocks */ + do { + + /* Clocks until next state change */ + until = clocks; + if (chan->clocks < until) /* Next sample */ + until = chan->clocks; + if ( + chan->env.enb && /* Modifications enabled */ + chan->env.clocks < until /* Next modification */ + ) until = chan->env.clocks; + if ( + chan->int_.auto_ && /* Shutoff enabled */ + chan->int_.clocks < until /* Shutoff */ + ) until = chan->int_.clocks; + if ( + index == 4 && /* Channel 5 */ + sim->vsu.freqmod.enb && /* Modifications enabled */ + sim->vsu.freqmod.interval != 0 && /* Modifications valid */ + sim->vsu.freqmod.clocks < until /* Next modification */ + ) until = sim->vsu.freqmod.clocks; + + /* Manage clocks */ + clocks -= until; + + /* Automatic shutoff */ + if ( + chan->int_.auto_ && /* Shutoff enabled */ + chan->int_.clocks == until + ) { + chan->int_.enb = 0; + return; + } + + /* Next sample */ + if (chan->clocks == until) { + + /* 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 && /* Modifications enabled */ + chan->env.clocks == until + ) { + 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; + else chan->env.enb = 0; + chan->env.clocks = (uint32_t) chan->env.interval * 307220; + } + + /* Frequency modification */ + if ( + index == 4 && /* Channel 5 */ + sim->vsu.freqmod.enb && /* Modifications enabled */ + sim->vsu.freqmod.interval != 0 && /* Modifications valid */ + sim->vsu.freqmod.clocks == until + ) { + 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 ? 19201 : 153610); + } + + } while (clocks != 0); + +} + +/* 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; + + /* Configure state */ + chan->env.clocks = (uint32_t) chan->env.interval * 307220; +} + +/* 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 >> 4 & 1; + vsuNextFreqMod(sim, chan); + 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; +} + +/* 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; +} + +/* 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; + chan->clocks = (index == 5 ? 40 : 4) * + ((uint32_t) 2048 - chan->freq.current); + if (index != 5) + chan->wave.sample = 0; + else sim->vsu.noise.register_ = 0x0000; + if (index == 4) { + sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval * + (sim->vsu.freqmod.clk ? 153610 : 19201); + 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 & 15; + sim->vsu.freqmod.shift = value & 7; + + /* Configure state */ + clocks = (uint32_t) sim->vsu.freqmod.interval * + (sim->vsu.freqmod.clk ? 153610 : 19201); + if (clocks < sim->vsu.freqmod.clocks) + sim->vsu.freqmod.clocks = clocks; +} + + + +/***************************** Module Functions ******************************/ + +/* Process component */ +static void vsuEmulate(VB *sim, uint32_t clocks) { + Channel *chan; /* Channel handle */ + uint32_t digital[2]; /* Digital output samples */ + float i0; /* Current analog input sample */ + float o0; /* Current analog output sample */ + 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; + + /* Working variables */ + clocks -= until; + digital[0] = digital[1] = 0; + + /* Process all channels */ + for (x = 0; x < 6; x++) { + chan = &sim->vsu.channels[x]; + vsuEmulateChannel(sim, x, until); + digital[0] += chan->output[0]; + digital[1] += chan->output[1]; + } + + /* Output sample */ + for (e = 0; e < 2; e++) { + + /* Compute the output sample for this stereo channel */ + i0 = (digital[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 + ) { + sim->vsu.out.samples[sim->vsu.out.offset++] = + (int16_t) (o0 * 32767.0f + (o0 < 0.0f ? -0.5f : +0.5f)); + 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->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; /* S5INT */ + case 0x544>>2: vsuWriteLRV(sim, 5, value); break; /* S5LRV */ + case 0x548>>2: vsuWriteFQL(sim, 5, value); break; /* S5FQL */ + case 0x54C>>2: vsuWriteFQH(sim, 5, value); break; /* S5FQH */ + case 0x550>>2: vsuWriteEV0(sim, 5, value); break; /* S5EV0 */ + case 0x554>>2: vsuWriteEV1(sim, 5, value); break; /* S5EV1 */ + + 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 */