ref: 6057aff755d8ad6ef6a0b624053a44eceeff4aab
parent: 0e56d8b9679d592f9d22fbc4b84e452f5a783404
author: allkern <lisandroaalarcon@gmail.com>
date: Sat Sep 30 20:47:04 EDT 2023
Massively improve SPU emulation
--- a/frontend/main.c
+++ b/frontend/main.c
@@ -14,10 +14,10 @@
psx_cdrom_get_cdda_samples(cdrom, buf, size);
for (int i = 0; i < (size >> 2); i++) {- int16_t sample = psx_spu_get_sample(spu);
+ int32_t sample = psx_spu_get_sample(spu) / 2;
- *(uint16_t*)(&buf[(i << 2) + 0]) = sample;
- *(uint16_t*)(&buf[(i << 2) + 2]) = sample;
+ *(uint16_t*)(&buf[(i << 2) + 0]) += sample;
+ *(uint16_t*)(&buf[(i << 2) + 2]) += sample;
}
}
--- a/psx/dev/dma.c
+++ b/psx/dev/dma.c
@@ -330,12 +330,12 @@
return;
log_set_quiet(0);
- log_fatal("SPU DMA transfer: madr=%08x, dir=%s, sync=%s (%u), step=%s, size=%x",+ log_fatal("SPU DMA transfer: madr=%08x, dir=%s, sync=%s (%u), step=%s, size=%x, blocks=%u",dma->spu.madr,
CHCR_TDIR(spu) ? "to device" : "to RAM",
g_psx_dma_sync_type_name_table[CHCR_SYNC(spu)], CHCR_SYNC(spu),
CHCR_STEP(spu) ? "decrementing" : "incrementing",
- BCR_SIZE(spu)
+ BCR_SIZE(spu), BCR_BCNT(spu)
);
log_fatal("DICR: force=%u, en=%02x, irqen=%u, flags=%02x",@@ -346,7 +346,8 @@
);
log_set_quiet(1);
- uint32_t size = BCR_SIZE(spu) * BCR_BCNT(spu);
+ uint32_t size = BCR_SIZE(spu);
+ uint32_t blocks = BCR_BCNT(spu);
if (!size) { log_fatal("0 sized SPU DMA");@@ -357,13 +358,15 @@
dma->spu_irq_delay = 1;
if (CHCR_TDIR(spu)) {- for (int i = 0; i < size; i++) {- uint32_t data = psx_bus_read32(dma->bus, dma->spu.madr);
+ for (int j = 0; j < blocks; j++) {+ for (int i = 0; i < size; i++) {+ uint32_t data = psx_bus_read32(dma->bus, dma->spu.madr);
- psx_bus_write16(dma->bus, 0x1f801da8, data >> 16);
- psx_bus_write16(dma->bus, 0x1f801da8, data & 0xffff);
+ psx_bus_write16(dma->bus, 0x1f801da8, data & 0xffff);
+ psx_bus_write16(dma->bus, 0x1f801da8, data >> 16);
- dma->spu.madr += CHCR_STEP(spu) ? -4 : 4;
+ dma->spu.madr += CHCR_STEP(spu) ? -4 : 4;
+ }
}
}
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -1704,6 +1704,8 @@
if (gpu->event_cb_table[GPU_EVENT_VBLANK_END])
gpu->event_cb_table[GPU_EVENT_VBLANK_END](gpu);
+ // psx_ic_irq(gpu->ic, IC_SPU);
+
gpu->line = 0;
}
}
--- a/psx/dev/mcd.c
+++ b/psx/dev/mcd.c
@@ -43,10 +43,6 @@
case 'S': mcd->state = MCD_S_STATE_TX_ACK1; break;
}
- /**/ log_set_quiet(0);
- /**/ log_fatal("mcd read %02x", mcd->tx_data);- /**/ log_set_quiet(1);
-
return mcd->tx_data;
} break;
@@ -71,10 +67,6 @@
break;
}
- /**/ log_set_quiet(0);
- /**/ log_fatal("mcd read %02x", data);- /**/ log_set_quiet(1);
-
return data;
} break;
case MCD_R_STATE_TX_CHK: mcd->tx_data = mcd->checksum; break;
@@ -82,10 +74,6 @@
mcd->tx_data_ready = 0;
mcd->state = MCD_STATE_TX_HIZ;
- /**/ log_set_quiet(0);
- /**/ log_fatal("mcd read %02x", 'G');- /**/ log_set_quiet(1);
-
return 'G';
} break;
}
@@ -93,18 +81,10 @@
mcd->tx_data_ready = 1;
mcd->state++;
- /**/ log_set_quiet(0);
- /**/ log_fatal("mcd read %02x", mcd->tx_data);- /**/ log_set_quiet(1);
-
return mcd->tx_data;
}
void psx_mcd_write(psx_mcd_t* mcd, uint8_t data) {- /**/ log_set_quiet(0);
- /**/ log_fatal("mcd write %02x", data);- /**/ log_set_quiet(1);
-
switch (mcd->state) {case MCD_STATE_TX_FLG: mcd->mode = data; break;
case MCD_R_STATE_RX_MSB: mcd->msb = data; break;
--- a/psx/dev/spu.c
+++ b/psx/dev/spu.c
@@ -5,7 +5,15 @@
#include "spu.h"
#include "../log.h"
-static const int16_t g_psx_spu_gauss_table[] = {+static const int g_spu_pos_adpcm_table[] = {+ 0, +60, +115, +98, +122
+};
+
+static const int g_spu_neg_adpcm_table[] = {+ 0, 0, -52, -55, -60
+};
+
+static const int16_t g_spu_gauss_table[] = {-0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001,
-0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001,
@@ -84,6 +92,8 @@
spu->ram = (uint8_t*)malloc(SPU_RAM_SIZE);
+ memset(spu->ram, 0, SPU_RAM_SIZE);
+
// Mute all voices
spu->endx = 0x00ffffff;
}
@@ -106,62 +116,116 @@
return 0x0;
}
-void psx_spu_write32(psx_spu_t* spu, uint32_t offset, uint32_t value) {- const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
+void spu_read_block(psx_spu_t* spu, int v) {+ uint32_t addr = spu->data[v].current_addr;
+ uint8_t hdr = spu->ram[addr];
- *((uint32_t*)(ptr + offset)) = value;
-}
+ spu->data[v].block_flags = spu->ram[addr + 1];
+ unsigned shift = 12 - (hdr & 0x0f);
+ unsigned filter = (hdr & 0x30) >> 4;
-void spu_read_block(psx_spu_t* spu, int v) {- spu->data[v].block_flags = spu->ram[spu->data[v].current_addr + 1];
+ int32_t f0 = g_spu_pos_adpcm_table[filter];
+ int32_t f1 = g_spu_neg_adpcm_table[filter];
+
+ for (int j = 0; j < 28; j++) {+ uint16_t n = (spu->ram[addr + 2 + (j >> 1)] >> ((j & 1) * 4)) & 0xf;
+
+ // Sign extend t
+ int16_t t = (int16_t)(n << 12) >> 12;
+ int16_t s = (t << shift) + (((spu->data[v].h[0] * f0) + (spu->data[v].h[1] * f1) + 32) / 64);
+
+ s = (s < INT16_MIN) ? INT16_MIN : ((s > INT16_MAX) ? INT16_MAX : s);
+
+ spu->data[v].h[1] = spu->data[v].h[0];
+ spu->data[v].h[0] = s;
+ spu->data[v].buf[j] = s;
+ }
}
-void psx_spu_write16(psx_spu_t* spu, uint32_t offset, uint16_t value) {- // Handle special cases first
+int spu_handle_write(psx_spu_t* spu, uint32_t offset, uint32_t value) { switch (offset) { case SPUR_KON: {- log_set_quiet(0);
+ if (!value)
+ return 1;
+
+ // log_set_quiet(0);
+ log_fatal("KON %04x:", value); for (int i = 0; i < 24; i++) {- if (value & (1 << i)) {- log_fatal("KON voice %2u s=%06x r=%06x p=%04x",+ if ((value & (1 << i))) {+ spu->data[i].playing = 1;
+ spu->data[i].current_addr = spu->voice[i].adsaddr << 3;
+ spu->data[i].repeat_addr = spu->data[i].current_addr;
+ spu->data[i].lvol = (float)(spu->voice[i].volumel << 1) / (float)0x7fff;
+ spu->data[i].rvol = (float)(spu->voice[i].volumer << 1) / (float)0x7fff;
+
+ log_fatal(" voice %2u s=%06x r=%06x p=%04x vol=%f,%f",i,
- spu->voice[i].adsaddr,
- spu->voice[i].adraddr,
- spu->voice[i].adsampr
+ spu->voice[i].adsaddr << 3,
+ spu->voice[i].adraddr << 3,
+ spu->voice[i].adsampr,
+ spu->data[i].lvol,
+ spu->data[i].rvol
);
- spu->data[i].current_addr = spu->voice[i].adsaddr;
-
spu_read_block(spu, i);
}
}
- log_set_quiet(1);
+ // log_set_quiet(1);
spu->endx &= ~(value & 0x00ffffff);
- } return;
+ } return 1;
case SPUR_KOFF: {- spu->endx |= value & 0x00ffffff;
- } return;
+ // log_set_quiet(0);
+ log_fatal("KOFF %04x:", value);+ for (int i = 0; i < 24; i++) {+ if ((value & (1 << i))) {+ spu->data[i].playing = 0;
+ log_fatal(" voice %2u",+ i,
+ spu->voice[i].adsaddr << 3,
+ spu->voice[i].adraddr << 3,
+ spu->voice[i].adsampr,
+ spu->data[i].lvol,
+ spu->data[i].rvol
+ );
+ }
+ }
+ // log_set_quiet(1);
+
+ //spu->endx |= value & 0x00ffffff;
+ } return 1;
+
case SPUR_TADDR: {spu->ramdta = value;
spu->taddr = value << 3;
- // log_set_quiet(0);
+ // // log_set_quiet(0);
// log_fatal("ramdta=%04x taddr=%08x", spu->ramdta, spu->taddr);- // log_set_quiet(1);
- } return;
+ // // log_set_quiet(1);
+ } return 1;
case SPUR_TFIFO: {- // log_set_quiet(0);
+ // // log_set_quiet(0);
// log_fatal("TFIFO write %04x (index=%u)", value, spu->tfifo_index);- // log_set_quiet(1);
+ // // log_set_quiet(1);
spu->ramdtf = value;
spu->tfifo[spu->tfifo_index++] = value;
- } return;
+ if (spu->tfifo_index == 32) {+ if (((spu->spucnt >> 4) & 3) == 2) {+ for (int i = 0; i < spu->tfifo_index; i++) {+ spu->ram[spu->taddr++] = spu->tfifo[i] & 0xff;
+ spu->ram[spu->taddr++] = spu->tfifo[i] >> 8;
+ }
+
+ spu->tfifo_index = 0;
+ }
+ }
+ } return 1;
+
case SPUR_SPUCNT: {spu->spucnt = value;
spu->spustat &= 0xffc0;
@@ -173,17 +237,35 @@
spu->ram[spu->taddr++] = spu->tfifo[i] >> 8;
}
- // log_set_quiet(0);
+ // // log_set_quiet(0);
// log_fatal("Transfer start mode=%u size=%u", (value >> 4) & 3, spu->tfifo_index);- // log_set_quiet(1);
+ // // log_set_quiet(1);
spu->tfifo_index = 0;
}
- } break;
+ } return 1;
}
+ return 0;
+}
+
+void psx_spu_write32(psx_spu_t* spu, uint32_t offset, uint32_t value) {+ // Handle special cases first
+ if (spu_handle_write(spu, offset, value))
+ return;
+
const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
+ *((uint32_t*)(ptr + offset)) = value;
+}
+
+void psx_spu_write16(psx_spu_t* spu, uint32_t offset, uint16_t value) {+ // Handle special cases first
+ if (spu_handle_write(spu, offset, value))
+ return;
+
+ const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
+
*((uint16_t*)(ptr + offset)) = value;
}
@@ -202,7 +284,7 @@
6-7 Unused (should be 0)
*/
-int16_t psx_spu_get_sample(psx_spu_t* spu) {+int32_t psx_spu_get_sample(psx_spu_t* spu) {if (spu->endx == 0x00ffffff)
return 0x0000;
@@ -210,7 +292,7 @@
int output = 0x0000;
for (int v = 0; v < 24; v++) {- if (spu->endx & (1 << v))
+ if (!spu->data[v].playing)
continue;
++voice_count;
@@ -230,12 +312,13 @@
switch (spu->data[v].block_flags & 3) { case 0: case 2: {- spu->data[v].current_addr += 2;
+ spu->data[v].current_addr += 0x10;
} break;
case 1: {spu->data[v].current_addr = spu->data[v].repeat_addr;
- spu->endx |= (1 << v);
+ //spu->endx |= (1 << v);
+ spu->data[v].playing = 0;
} break;
case 3: {@@ -249,25 +332,15 @@
spu_read_block(spu, v);
}
- uint8_t hdr = spu->ram[spu->data[v].current_addr];
-
// Fetch ADPCM sample
- uint16_t sample = spu->ram[spu->data[v].current_addr + 2 + (sample_index >> 1)];
+ spu->data[v].s[0] = spu->data[v].buf[sample_index];
- // "Expand" sample (according to XA-ADPCM)
- sample >>= (sample_index & 1) * 4;
- sample &= 0xf;
- sample <<= 12;
- sample >>= hdr & 0xf;
-
- spu->data[v].s[0] = sample;
-
// Apply 4-point Gaussian interpolation
uint8_t gauss_index = (spu->data[v].counter >> 4) & 0xff;
- int16_t g0 = g_psx_spu_gauss_table[0x0ff - gauss_index];
- int16_t g1 = g_psx_spu_gauss_table[0x1ff - gauss_index];
- int16_t g2 = g_psx_spu_gauss_table[0x100 + gauss_index];
- int16_t g3 = g_psx_spu_gauss_table[0x000 + gauss_index];
+ int16_t g0 = g_spu_gauss_table[0x0ff - gauss_index];
+ int16_t g1 = g_spu_gauss_table[0x1ff - gauss_index];
+ int16_t g2 = g_spu_gauss_table[0x100 + gauss_index];
+ int16_t g3 = g_spu_gauss_table[0x000 + gauss_index];
int16_t out;
out = (g0 * spu->data[v].s[3]) >> 15;
@@ -275,9 +348,9 @@
out += (g2 * spu->data[v].s[1]) >> 15;
out += (g3 * spu->data[v].s[0]) >> 15;
- output += out;
+ output += out * ((spu->data[v].lvol + spu->data[v].rvol) / 2);
- uint16_t step = spu->voice[0].adsampr;
+ uint16_t step = spu->voice[v].adsampr;
/* To-do: Do pitch modulation here */
@@ -284,5 +357,10 @@
spu->data[v].counter += step;
}
- return output / voice_count;
+ if (!voice_count)
+ return 0x00000000;
+
+ output = (output < INT16_MIN) ? INT16_MIN : ((output > INT16_MAX) ? INT16_MAX : output);
+
+ return output;
}
\ No newline at end of file
--- a/psx/dev/spu.h
+++ b/psx/dev/spu.h
@@ -105,11 +105,16 @@
uint16_t tfifo_index;
struct {+ int playing;
uint32_t counter;
uint32_t current_addr;
uint32_t repeat_addr;
int16_t s[4];
int block_flags;
+ int16_t buf[28];
+ int16_t h[2];
+ float lvol;
+ float rvol;
} data[24];
} psx_spu_t;
@@ -122,6 +127,6 @@
void psx_spu_write16(psx_spu_t*, uint32_t, uint16_t);
void psx_spu_write8(psx_spu_t*, uint32_t, uint8_t);
void psx_spu_destroy(psx_spu_t*);
-int16_t psx_spu_get_sample(psx_spu_t*);
+int32_t psx_spu_get_sample(psx_spu_t*);
#endif
\ No newline at end of file
--
⑨