From 9bd15bd513fab980706383f00cab338c649388d6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 10 Mar 2022 15:51:27 -0500 Subject: [PATCH] VERA: bring up actual emulation core --- CMakeLists.txt | 2 + src/engine/platform/sound/vera_pcm.c | 130 +++++++++++++++++++++++++++ src/engine/platform/sound/vera_pcm.h | 31 +++++++ src/engine/platform/sound/vera_psg.c | 98 ++++++++++++++++++++ src/engine/platform/sound/vera_psg.h | 27 ++++++ src/engine/platform/vera.cpp | 11 ++- src/engine/platform/vera.h | 7 +- 7 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 src/engine/platform/sound/vera_pcm.c create mode 100644 src/engine/platform/sound/vera_pcm.h create mode 100644 src/engine/platform/sound/vera_psg.c create mode 100644 src/engine/platform/sound/vera_psg.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 487d8617c..e3dbdbbaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,6 +234,8 @@ src/engine/platform/sound/gb/apu.c src/engine/platform/sound/gb/timing.c src/engine/platform/sound/pce_psg.cpp src/engine/platform/sound/nes/apu.c +src/engine/platform/sound/vera_psg.c +src/engine/platform/sound/vera_pcm.c src/engine/platform/sound/c64/sid.cc src/engine/platform/sound/c64/voice.cc diff --git a/src/engine/platform/sound/vera_pcm.c b/src/engine/platform/sound/vera_pcm.c new file mode 100644 index 000000000..2a95ff04e --- /dev/null +++ b/src/engine/platform/sound/vera_pcm.c @@ -0,0 +1,130 @@ +// Commander X16 Emulator +// Copyright (c) 2020 Frank van den Hoef +// All rights reserved. License: 2-clause BSD + +#include "vera_pcm.h" +#include + +static uint8_t volume_lut[16] = {0, 1, 2, 3, 4, 5, 6, 8, 11, 14, 18, 23, 30, 38, 49, 64}; + +static void +fifo_reset(struct VERA_PCM* pcm) +{ + pcm->fifo_wridx = 0; + pcm->fifo_rdidx = 0; + pcm->fifo_cnt = 0; +} + +void +pcm_reset(struct VERA_PCM* pcm) +{ + fifo_reset(pcm); + pcm->ctrl = 0; + pcm->rate = 0; + pcm->cur_l = 0; + pcm->cur_r = 0; + pcm->phase = 0; +} + +void +pcm_write_ctrl(struct VERA_PCM* pcm, uint8_t val) +{ + if (val & 0x80) { + fifo_reset(pcm); + } + + pcm->ctrl = val & 0x3F; +} + +uint8_t +pcm_read_ctrl(struct VERA_PCM* pcm) +{ + uint8_t result = pcm->ctrl; + if (pcm->fifo_cnt == sizeof(pcm->fifo)) { + result |= 0x80; + } + return result; +} + +void +pcm_write_rate(struct VERA_PCM* pcm, uint8_t val) +{ + pcm->rate = val; +} + +uint8_t +pcm_read_rate(struct VERA_PCM* pcm) +{ + return pcm->rate; +} + +void +pcm_write_fifo(struct VERA_PCM* pcm, uint8_t val) +{ + if (pcm->fifo_cnt < sizeof(pcm->fifo)) { + pcm->fifo[pcm->fifo_wridx++] = val; + if (pcm->fifo_wridx == sizeof(pcm->fifo)) { + pcm->fifo_wridx = 0; + } + pcm->fifo_cnt++; + } +} + +static uint8_t +read_fifo(struct VERA_PCM* pcm) +{ + if (pcm->fifo_cnt == 0) { + return 0; + } + uint8_t result = pcm->fifo[pcm->fifo_rdidx++]; + if (pcm->fifo_rdidx == sizeof(pcm->fifo)) { + pcm->fifo_rdidx = 0; + } + pcm->fifo_cnt--; + return result; +} + +bool +pcm_is_fifo_almost_empty(struct VERA_PCM* pcm) +{ + return pcm->fifo_cnt < 1024; +} + +void +pcm_render(struct VERA_PCM* pcm, int16_t* buf_l, int16_t* buf_r, unsigned num_samples) +{ + while (num_samples--) { + uint8_t old_phase = pcm->phase; + pcm->phase += pcm->rate; + if ((old_phase & 0x80) != (pcm->phase & 0x80)) { + switch ((pcm->ctrl >> 4) & 3) { + case 0: { // mono 8-bit + pcm->cur_l = (int16_t)read_fifo(pcm) << 8; + pcm->cur_r = pcm->cur_l; + break; + } + case 1: { // stereo 8-bit + pcm->cur_l = read_fifo(pcm) << 8; + pcm->cur_r = read_fifo(pcm) << 8; + break; + } + case 2: { // mono 16-bit + pcm->cur_l = read_fifo(pcm); + pcm->cur_l |= read_fifo(pcm) << 8; + pcm->cur_r = pcm->cur_l; + break; + } + case 3: { // stereo 16-bit + pcm->cur_l = read_fifo(pcm); + pcm->cur_l |= read_fifo(pcm) << 8; + pcm->cur_r = read_fifo(pcm); + pcm->cur_r |= read_fifo(pcm) << 8; + break; + } + } + } + + *(buf_l++) = ((int)pcm->cur_l * (int)volume_lut[pcm->ctrl & 0xF]) >> 6; + *(buf_r++) = ((int)pcm->cur_r * (int)volume_lut[pcm->ctrl & 0xF]) >> 6; + } +} diff --git a/src/engine/platform/sound/vera_pcm.h b/src/engine/platform/sound/vera_pcm.h new file mode 100644 index 000000000..d9b600d0b --- /dev/null +++ b/src/engine/platform/sound/vera_pcm.h @@ -0,0 +1,31 @@ +// Commander X16 Emulator +// Copyright (c) 2020 Frank van den Hoef +// All rights reserved. License: 2-clause BSD + +#pragma once + +#include +#include + +struct VERA_PCM { + uint8_t fifo[4096 - 1]; // Actual hardware FIFO is 4kB, but you can only use 4095 bytes. + unsigned fifo_wridx; + unsigned fifo_rdidx; + unsigned fifo_cnt; + + uint8_t ctrl; + uint8_t rate; + + int16_t cur_l, cur_r; + uint8_t phase; +}; + + +void pcm_reset(struct VERA_PCM* pcm); +void pcm_write_ctrl(struct VERA_PCM* pcm, uint8_t val); +uint8_t pcm_read_ctrl(struct VERA_PCM* pcm); +void pcm_write_rate(struct VERA_PCM* pcm, uint8_t val); +uint8_t pcm_read_rate(struct VERA_PCM* pcm); +void pcm_write_fifo(struct VERA_PCM* pcm, uint8_t val); +void pcm_render(struct VERA_PCM* pcm, int16_t* buf_l, int16_t* buf_r, unsigned num_samples); +bool pcm_is_fifo_almost_empty(struct VERA_PCM* pcm); diff --git a/src/engine/platform/sound/vera_psg.c b/src/engine/platform/sound/vera_psg.c new file mode 100644 index 000000000..92dac698c --- /dev/null +++ b/src/engine/platform/sound/vera_psg.c @@ -0,0 +1,98 @@ +// Commander X16 Emulator +// Copyright (c) 2020 Frank van den Hoef +// All rights reserved. License: 2-clause BSD + +#include "vera_psg.h" + +#include +#include + +enum waveform { + WF_PULSE = 0, + WF_SAWTOOTH, + WF_TRIANGLE, + WF_NOISE, +}; + +static uint8_t volume_lut[64] = {0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, 13, 14, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 28, 29, 31, 33, 35, 37, 39, 42, 44, 47, 50, 52, 56, 59, 63}; + +void +psg_reset(struct VERA_PSG* psg) +{ + memset(psg->channels, 0, sizeof(psg->channels)); +} + +void +psg_writereg(struct VERA_PSG* psg, uint8_t reg, uint8_t val) +{ + reg &= 0x3f; + + int ch = reg / 4; + int idx = reg & 3; + + switch (idx) { + case 0: psg->channels[ch].freq = (psg->channels[ch].freq & 0xFF00) | val; break; + case 1: psg->channels[ch].freq = (psg->channels[ch].freq & 0x00FF) | (val << 8); break; + case 2: { + psg->channels[ch].right = (val & 0x80) != 0; + psg->channels[ch].left = (val & 0x40) != 0; + psg->channels[ch].volume = volume_lut[val & 0x3F]; + break; + } + case 3: { + psg->channels[ch].pw = val & 0x3F; + psg->channels[ch].waveform = val >> 6; + break; + } + } +} + +static inline void +render(struct VERA_PSG* psg, int16_t *left, int16_t *right) +{ + int l = 0; + int r = 0; + + for (int i = 0; i < 16; i++) { + struct VERAChannel *ch = &psg->channels[i]; + + unsigned new_phase = (ch->phase + ch->freq) & 0x1FFFF; + if ((ch->phase & 0x10000) != (new_phase & 0x10000)) { + ch->noiseval = rand() & 63; + } + ch->phase = new_phase; + + uint8_t v = 0; + switch (ch->waveform) { + case WF_PULSE: v = (ch->phase >> 10) > ch->pw ? 0 : 63; break; + case WF_SAWTOOTH: v = ch->phase >> 11; break; + case WF_TRIANGLE: v = (ch->phase & 0x10000) ? (~(ch->phase >> 10) & 0x3F) : ((ch->phase >> 10) & 0x3F); break; + case WF_NOISE: v = ch->noiseval; break; + } + int8_t sv = (v ^ 0x20); + if (sv & 0x20) { + sv |= 0xC0; + } + + int val = (int)sv * (int)ch->volume; + + if (ch->left) { + l += val; + } + if (ch->right) { + r += val; + } + } + + *left = l; + *right = r; +} + +void +psg_render(struct VERA_PSG* psg, int16_t *buf, unsigned num_samples) +{ + while (num_samples--) { + render(psg, &buf[0], &buf[1]); + buf += 2; + } +} diff --git a/src/engine/platform/sound/vera_psg.h b/src/engine/platform/sound/vera_psg.h new file mode 100644 index 000000000..9f94bb578 --- /dev/null +++ b/src/engine/platform/sound/vera_psg.h @@ -0,0 +1,27 @@ +// Commander X16 Emulator +// Copyright (c) 2020 Frank van den Hoef +// All rights reserved. License: 2-clause BSD + +#pragma once + +#include +#include + +struct VERAChannel { + uint16_t freq; + uint8_t volume; + bool left, right; + uint8_t pw; + uint8_t waveform; + + unsigned phase; + uint8_t noiseval; +}; + +struct VERA_PSG { + struct VERAChannel channels[16]; +}; + +void psg_reset(struct VERA_PSG* psg); +void psg_writereg(struct VERA_PSG* psg, uint8_t reg, uint8_t val); +void psg_render(struct VERA_PSG* psg, int16_t *buf, unsigned num_samples); diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index e58266dd6..99c6517ea 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -22,6 +22,9 @@ #include #include +#include "sound/vera_psg.h" +#include "sound/vera_pcm.h" + #define rWrite(c,a,d) {regPool[(c)*4+(a)]=(d);} #define rWriteLo(c,a,d) rWrite(c,a,(regPool[(c)*4+(a)]&(~0x3f))|((d)&0x3f)) #define rWriteHi(c,a,d) rWrite(c,a,(regPool[(c)*4+(a)]&(~0xc0))|(((d)<<6)&0xc0)) @@ -69,8 +72,6 @@ void DivPlatformVERA::reset() { } chan[16].vol=15; chan[16].pan=3; - noiseState=1; - noiseOut=0; } int DivPlatformVERA::calcNoteFreq(int ch, int note) { @@ -321,6 +322,8 @@ int DivPlatformVERA::init(DivEngine* p, int channels, int sugRate, unsigned int isMuted[i]=false; } parent=p; + psg=new struct VERA_PSG; + pcm=new struct VERA_PCM; dumpWrites=false; skipRegisterWrites=false; chipClock=25000000; @@ -329,5 +332,9 @@ int DivPlatformVERA::init(DivEngine* p, int channels, int sugRate, unsigned int return 17; } +void DivPlatformVERA::quit() { + delete psg; + delete pcm; +} DivPlatformVERA::~DivPlatformVERA() { } diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index e5cb2ad9e..e1369301b 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -23,6 +23,9 @@ #include "../instrument.h" #include "../macroInt.h" +struct VERA_PSG; +struct VERA_PCM; + class DivPlatformVERA: public DivDispatch { protected: struct Channel { @@ -46,8 +49,9 @@ class DivPlatformVERA: public DivDispatch { }; Channel chan[17]; bool isMuted[17]; - unsigned noiseState, noiseOut; unsigned char regPool[66]; + struct VERA_PSG* psg; + struct VERA_PCM* pcm; int calcNoteFreq(int ch, int note); friend void putDispatchChan(void*,int,int); @@ -68,6 +72,7 @@ class DivPlatformVERA: public DivDispatch { const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); ~DivPlatformVERA(); }; #endif