+#if defined(THREAD_ENABLED) || defined(WANT_THREAD_CODE)
+
+// worker thread state
+static struct spu_worker {
+ union {
+ struct {
+ unsigned int exit_thread;
+ unsigned int i_ready;
+ unsigned int i_reaped;
+ unsigned int last_boot_cnt; // dsp
+ unsigned int ram_dirty;
+ };
+ // aligning for C64X_DSP
+ unsigned int _pad0[128/4];
+ };
+ union {
+ struct {
+ unsigned int i_done;
+ unsigned int active; // dsp
+ unsigned int boot_cnt;
+ };
+ unsigned int _pad1[128/4];
+ };
+ struct work_item {
+ int ns_to;
+ int ctrl;
+ int decode_pos;
+ int rvb_addr;
+ unsigned int channels_new;
+ unsigned int channels_on;
+ unsigned int channels_silent;
+ struct {
+ int spos;
+ int sbpos;
+ int sinc;
+ int start;
+ int loop;
+ int ns_to;
+ short vol_l;
+ short vol_r;
+ ADSRInfoEx adsr;
+ // might also want to add fmod flags..
+ } ch[24];
+ int SSumLR[NSSIZE * 2];
+ } i[4];
+} *worker;
+
+#define WORK_MAXCNT (sizeof(worker->i) / sizeof(worker->i[0]))
+#define WORK_I_MASK (WORK_MAXCNT - 1)
+
+static void thread_work_start(void);
+static void thread_work_wait_sync(struct work_item *work, int force);
+static void thread_sync_caches(void);
+static int thread_get_i_done(void);
+
+static int decode_block_work(void *context, int ch, int *SB)
+{
+ const unsigned char *ram = spu.spuMemC;
+ int predict_nr, shift_factor, flags;
+ struct work_item *work = context;
+ int start = work->ch[ch].start;
+ int loop = work->ch[ch].loop;
+
+ predict_nr = ram[start];
+ shift_factor = predict_nr & 0xf;
+ predict_nr >>= 4;
+
+ decode_block_data(SB, ram + start + 2, predict_nr, shift_factor);
+
+ flags = ram[start + 1];
+ if (flags & 4)
+ loop = start; // loop adress
+
+ start += 16;
+
+ if (flags & 1) // 1: stop/loop
+ start = loop;
+
+ work->ch[ch].start = start & 0x7ffff;
+ work->ch[ch].loop = loop;