diff --git a/CMakeLists.txt b/CMakeLists.txt index b9cae5352..b851ce593 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -645,6 +645,10 @@ src/engine/platform/sound/ga20/iremga20.cpp src/engine/platform/sound/sm8521.c +src/engine/platform/sound/supervision.c + +src/engine/platform/sound/upd1771c.c + src/engine/platform/sound/d65modified.c src/engine/platform/sound/ted-sound.c @@ -778,6 +782,8 @@ src/engine/platform/snes.cpp src/engine/platform/k007232.cpp src/engine/platform/ga20.cpp src/engine/platform/sm8521.cpp +src/engine/platform/supervision.cpp +src/engine/platform/upd1771c.cpp src/engine/platform/pv1000.cpp src/engine/platform/k053260.cpp src/engine/platform/ted.cpp diff --git a/doc/1-intro/qs-insteditor.png b/doc/1-intro/qs-insteditor.png index e00dabe0c..a28fda719 100644 Binary files a/doc/1-intro/qs-insteditor.png and b/doc/1-intro/qs-insteditor.png differ diff --git a/doc/1-intro/qs-instlist-empty.png b/doc/1-intro/qs-instlist-empty.png index 644cae3b8..ef87dc31c 100644 Binary files a/doc/1-intro/qs-instlist-empty.png and b/doc/1-intro/qs-instlist-empty.png differ diff --git a/doc/1-intro/qs-instlist-full.png b/doc/1-intro/qs-instlist-full.png index fa7fe3d52..e282f9f4b 100644 Binary files a/doc/1-intro/qs-instlist-full.png and b/doc/1-intro/qs-instlist-full.png differ diff --git a/doc/1-intro/qs-instlist-lessempty.png b/doc/1-intro/qs-instlist-lessempty.png index ec2005c9d..81beaf6e8 100644 Binary files a/doc/1-intro/qs-instlist-lessempty.png and b/doc/1-intro/qs-instlist-lessempty.png differ diff --git a/doc/1-intro/qs-interface.png b/doc/1-intro/qs-interface.png index 5be5597e7..e1e7967a2 100644 Binary files a/doc/1-intro/qs-interface.png and b/doc/1-intro/qs-interface.png differ diff --git a/doc/1-intro/qs-macro-release.png b/doc/1-intro/qs-macro-release.png index 659982f3b..b8fd2d899 100644 Binary files a/doc/1-intro/qs-macro-release.png and b/doc/1-intro/qs-macro-release.png differ diff --git a/doc/1-intro/qs-macro.png b/doc/1-intro/qs-macro.png index a4481aba3..ef29ebbd7 100644 Binary files a/doc/1-intro/qs-macro.png and b/doc/1-intro/qs-macro.png differ diff --git a/doc/1-intro/qs-noteoffs-channels.png b/doc/1-intro/qs-noteoffs-channels.png index e8405df23..b0c2b867c 100644 Binary files a/doc/1-intro/qs-noteoffs-channels.png and b/doc/1-intro/qs-noteoffs-channels.png differ diff --git a/doc/1-intro/qs-notes-channel.png b/doc/1-intro/qs-notes-channel.png index cbf84d307..3df201f9c 100644 Binary files a/doc/1-intro/qs-notes-channel.png and b/doc/1-intro/qs-notes-channel.png differ diff --git a/doc/1-intro/qs-notes-channels.png b/doc/1-intro/qs-notes-channels.png index 77bf3b16e..d8ef4c81b 100644 Binary files a/doc/1-intro/qs-notes-channels.png and b/doc/1-intro/qs-notes-channels.png differ diff --git a/doc/1-intro/qs-notes-effect.png b/doc/1-intro/qs-notes-effect.png index 1d88ee485..744d58de6 100644 Binary files a/doc/1-intro/qs-notes-effect.png and b/doc/1-intro/qs-notes-effect.png differ diff --git a/doc/1-intro/qs-notes-inst.png b/doc/1-intro/qs-notes-inst.png index 2c0c2e9bf..a0c97caee 100644 Binary files a/doc/1-intro/qs-notes-inst.png and b/doc/1-intro/qs-notes-inst.png differ diff --git a/doc/1-intro/qs-notes-spaced.png b/doc/1-intro/qs-notes-spaced.png index 507dec35b..e07fffb24 100644 Binary files a/doc/1-intro/qs-notes-spaced.png and b/doc/1-intro/qs-notes-spaced.png differ diff --git a/doc/1-intro/qs-notes-vol.png b/doc/1-intro/qs-notes-vol.png index 5644da1a0..a44c43032 100644 Binary files a/doc/1-intro/qs-notes-vol.png and b/doc/1-intro/qs-notes-vol.png differ diff --git a/doc/1-intro/qs-notes-wronginst.png b/doc/1-intro/qs-notes-wronginst.png index b92e6a70d..2c8288f67 100644 Binary files a/doc/1-intro/qs-notes-wronginst.png and b/doc/1-intro/qs-notes-wronginst.png differ diff --git a/doc/1-intro/qs-order-added.png b/doc/1-intro/qs-order-added.png index 372968f46..ac1b0e6cb 100644 Binary files a/doc/1-intro/qs-order-added.png and b/doc/1-intro/qs-order-added.png differ diff --git a/doc/1-intro/qs-order-changed.png b/doc/1-intro/qs-order-changed.png index 47abdcc6c..b47c84c2d 100644 Binary files a/doc/1-intro/qs-order-changed.png and b/doc/1-intro/qs-order-changed.png differ diff --git a/doc/1-intro/qs-order-default.png b/doc/1-intro/qs-order-default.png index e7823b0f8..23c532f94 100644 Binary files a/doc/1-intro/qs-order-default.png and b/doc/1-intro/qs-order-default.png differ diff --git a/doc/1-intro/qs-playeditcontrols.png b/doc/1-intro/qs-playeditcontrols.png index 7e65fcef4..234a4ab3f 100644 Binary files a/doc/1-intro/qs-playeditcontrols.png and b/doc/1-intro/qs-playeditcontrols.png differ diff --git a/doc/1-intro/qs-samplist.png b/doc/1-intro/qs-samplist.png index cfe4990e8..0a681c8b0 100644 Binary files a/doc/1-intro/qs-samplist.png and b/doc/1-intro/qs-samplist.png differ diff --git a/doc/1-intro/qs-selection.png b/doc/1-intro/qs-selection.png index fb7a841a0..bb85b9032 100644 Binary files a/doc/1-intro/qs-selection.png and b/doc/1-intro/qs-selection.png differ diff --git a/doc/1-intro/qs-speed.png b/doc/1-intro/qs-speed.png index 19a9e109d..e6c1bf8f5 100644 Binary files a/doc/1-intro/qs-speed.png and b/doc/1-intro/qs-speed.png differ diff --git a/doc/1-intro/qs-waveeditor.png b/doc/1-intro/qs-waveeditor.png index 33ad7dfbe..c6829fc05 100644 Binary files a/doc/1-intro/qs-waveeditor.png and b/doc/1-intro/qs-waveeditor.png differ diff --git a/doc/1-intro/qs-wavelist.png b/doc/1-intro/qs-wavelist.png index fd4f85062..17255c2f6 100644 Binary files a/doc/1-intro/qs-wavelist.png and b/doc/1-intro/qs-wavelist.png differ diff --git a/doc/2-interface/docking.png b/doc/2-interface/docking.png index 35b828688..03a143291 100644 Binary files a/doc/2-interface/docking.png and b/doc/2-interface/docking.png differ diff --git a/doc/2-interface/effect-list.png b/doc/2-interface/effect-list.png index dd5d0cc8b..c87b19256 100644 Binary files a/doc/2-interface/effect-list.png and b/doc/2-interface/effect-list.png differ diff --git a/doc/2-interface/interface1.png b/doc/2-interface/interface1.png index 47160cae5..dced4ef60 100644 Binary files a/doc/2-interface/interface1.png and b/doc/2-interface/interface1.png differ diff --git a/doc/3-pattern/channelbar.png b/doc/3-pattern/channelbar.png index dc7193e39..e22870603 100644 Binary files a/doc/3-pattern/channelbar.png and b/doc/3-pattern/channelbar.png differ diff --git a/doc/3-pattern/pattern.png b/doc/3-pattern/pattern.png index 0d31fcf1f..7556df6b2 100644 Binary files a/doc/3-pattern/pattern.png and b/doc/3-pattern/pattern.png differ diff --git a/doc/4-instrument/instrument-editor-top.png b/doc/4-instrument/instrument-editor-top.png index 3cfab1ec1..7c032326d 100644 Binary files a/doc/4-instrument/instrument-editor-top.png and b/doc/4-instrument/instrument-editor-top.png differ diff --git a/doc/4-instrument/sample-map.png b/doc/4-instrument/sample-map.png index 03e12c2cf..5598d429f 100644 Binary files a/doc/4-instrument/sample-map.png and b/doc/4-instrument/sample-map.png differ diff --git a/doc/6-sample/sample-editor.png b/doc/6-sample/sample-editor.png index 30dce72da..7c8d9b50c 100644 Binary files a/doc/6-sample/sample-editor.png and b/doc/6-sample/sample-editor.png differ diff --git a/doc/8-advanced/channels.png b/doc/8-advanced/channels.png index 31afb7c6c..9fc26fde5 100644 Binary files a/doc/8-advanced/channels.png and b/doc/8-advanced/channels.png differ diff --git a/doc/8-advanced/chanosc-gradient.png b/doc/8-advanced/chanosc-gradient.png index 0a413d266..4d3c973e5 100644 Binary files a/doc/8-advanced/chanosc-gradient.png and b/doc/8-advanced/chanosc-gradient.png differ diff --git a/doc/8-advanced/chanosc.png b/doc/8-advanced/chanosc.png index 8128b1d8e..feeb8cd5e 100644 Binary files a/doc/8-advanced/chanosc.png and b/doc/8-advanced/chanosc.png differ diff --git a/doc/8-advanced/chip-manager.png b/doc/8-advanced/chip-manager.png index b746d85d0..208a2fa7f 100644 Binary files a/doc/8-advanced/chip-manager.png and b/doc/8-advanced/chip-manager.png differ diff --git a/doc/8-advanced/find-find.png b/doc/8-advanced/find-find.png index 1d5e05ba9..69889f360 100644 Binary files a/doc/8-advanced/find-find.png and b/doc/8-advanced/find-find.png differ diff --git a/doc/8-advanced/find-replace.png b/doc/8-advanced/find-replace.png index a9206beae..95f54b297 100644 Binary files a/doc/8-advanced/find-replace.png and b/doc/8-advanced/find-replace.png differ diff --git a/doc/8-advanced/grooves.png b/doc/8-advanced/grooves.png index e38e944f0..db3d19301 100644 Binary files a/doc/8-advanced/grooves.png and b/doc/8-advanced/grooves.png differ diff --git a/doc/8-advanced/memcompo.png b/doc/8-advanced/memcompo.png new file mode 100644 index 000000000..f9ae5f948 Binary files /dev/null and b/doc/8-advanced/memcompo.png differ diff --git a/doc/8-advanced/piano.png b/doc/8-advanced/piano.png index 9b2b7b4c3..4773fef74 100644 Binary files a/doc/8-advanced/piano.png and b/doc/8-advanced/piano.png differ diff --git a/doc/8-advanced/stats.png b/doc/8-advanced/stats.png index 6e4ec92a0..7a99aff14 100644 Binary files a/doc/8-advanced/stats.png and b/doc/8-advanced/stats.png differ diff --git a/doc/8-advanced/user-systems.png b/doc/8-advanced/user-systems.png index c300530a3..b5a3fd491 100644 Binary files a/doc/8-advanced/user-systems.png and b/doc/8-advanced/user-systems.png differ diff --git a/doc/8-advanced/xyosc.md b/doc/8-advanced/xyosc.md index e03a1f945..9b238fb65 100644 --- a/doc/8-advanced/xyosc.md +++ b/doc/8-advanced/xyosc.md @@ -2,7 +2,7 @@ also called "vectorscope", this is similar to the normal oscilloscope in that it draws audio output as a waveform, but instead of being one-dimensional and going from left to right, it is plotted in two dimensions (usually X represents left output and Y represents right output). -(TODO: image) +![oscilloscope (X-Y)](xyosc.png) right-clicking the X-Y oscilloscope window displays settings: - **X Channel**: the output channel which will affect the X (horizontal) axis. default is 1 (left). diff --git a/doc/8-advanced/xyosc.png b/doc/8-advanced/xyosc.png new file mode 100644 index 000000000..dc49e74d1 Binary files /dev/null and b/doc/8-advanced/xyosc.png differ diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index d1efa70ef..8c2784f2b 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -76,6 +76,8 @@ #include "platform/vb.h" #include "platform/k007232.h" #include "platform/ga20.h" +#include "platform/supervision.h" +#include "platform/upd1771c.h" #include "platform/sm8521.h" #include "platform/pv1000.h" #include "platform/k053260.h" @@ -684,6 +686,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_GA20: dispatch=new DivPlatformGA20; break; + case DIV_SYSTEM_SUPERVISION: + dispatch=new DivPlatformSupervision; + break; + case DIV_SYSTEM_UPD1771C: + dispatch=new DivPlatformUPD1771c; + break; case DIV_SYSTEM_SM8521: dispatch=new DivPlatformSM8521; if (isRender) { diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index ef1848260..ed5894d65 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -1283,6 +1283,12 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo feature64=true; featureS2=true; break; + case DIV_INS_SUPERVISION: + featureSM=true; + if (amiga.useSample) featureSL=true; + break; + case DIV_INS_UPD1771C: + break; case DIV_INS_MAX: break; case DIV_INS_NULL: diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 648aeb5c1..8115d81ab 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -96,6 +96,8 @@ enum DivInstrumentType: unsigned short { DIV_INS_GBA_MINMOD=61, DIV_INS_BIFURCATOR=62, DIV_INS_SID2=63, // coincidence! + DIV_INS_SUPERVISION=64, + DIV_INS_UPD1771C=65, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/platform/sound/supervision.c b/src/engine/platform/sound/supervision.c new file mode 100644 index 000000000..c042ba313 --- /dev/null +++ b/src/engine/platform/sound/supervision.c @@ -0,0 +1,282 @@ +// THIS IS A MODIFIED VERSION OF POTATOR'S SOUND EMULATION CORE +// MODIFIED BY AART1256 IN 2024 + +#include "supervision.h" + +#include + +#define SV_SAMPLE_RATE ((svision->UNSCALED_CLOCK)/64) +#define SV_DEC_TICK ((SV_SAMPLE_RATE)/60) + +void supervision_sound_set_clock(struct svision_t *svision, uint32 clock) { + svision->UNSCALED_CLOCK = clock; +} + +void supervision_memorymap_registers_write(struct svision_t *svision, uint32 Addr, uint8 Value) +{ + switch (Addr & 0x1fff) { + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + supervision_sound_wave_write(svision, ((Addr & 0x4) >> 2), Addr & 3, Value); + break; + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + supervision_sound_dma_write(svision, Addr & 0x07, Value); + break; + case 0x28: + case 0x29: + case 0x2a: + supervision_sound_noise_write(svision, Addr & 0x07, Value); + break; + } +} + +void supervision_set_mute_mask(struct svision_t *svision, uint8 mask) { + svision->ch_mask = mask; +} + +void supervision_sound_set_flags(struct svision_t *svision, uint8 flags_set) +{ + svision->flags = flags_set; +} + +void supervision_sound_reset(struct svision_t *svision) +{ + memset(svision->m_channel, 0, sizeof(svision->m_channel)); + memset(&svision->m_noise, 0, sizeof(svision->m_noise) ); + memset(&svision->m_dma, 0, sizeof(svision->m_dma) ); + + memset(svision->ch, 0, sizeof(svision->ch) ); + svision->decrement_tick = 0; + svision->ch_mask = 15; +} + +void supervision_sound_stream_update(struct svision_t *svision, uint8 *stream, uint32 len) +{ + size_t i, j; + SVISION_CHANNEL *channel; + uint8 s = 0; + uint8 *left = stream + 0; + uint8 *right = stream + 1; + uint8 *chout = stream + 2; + + for (i = 0; i < len >> 1; i++, left += 2, right += 2) { + *left = *right = 0; + for (channel = svision->m_channel, j = 0; j < 2; j++, channel++) { + chout[j] = 0; + if (svision->ch[j].size != 0) { + if (svision->ch[j].on || channel->count != 0) { + BOOL on = 0; + switch (svision->ch[j].waveform) { + case 0: // 12.5% + on = svision->ch[j].pos < (28 * svision->ch[j].size) >> 5; + break; + case 1: // 25% + on = svision->ch[j].pos < (24 * svision->ch[j].size) >> 5; + break; + case 2: // 50% + on = svision->ch[j].pos < svision->ch[j].size / 2; + break; + case 3: // 75% + on = svision->ch[j].pos < svision->ch[j].size / 4; + // MESS/MAME: <= (9 * svision->ch[j].size) >> 5; + break; + } + s = on ? (svision->ch[j].volume)<<2 : 0; + s = ((svision->ch_mask>>(3-j))&1)?s:0; + if (svision->flags&1) { + if (j == 0) + *right += s; + else + *left += s; + } else { + *left += s; + *right += s; + } + chout[j] = s; + } + svision->ch[j].pos++; + if (svision->ch[j].pos >= svision->ch[j].size) { + svision->ch[j].pos = 0; + // Transition from off to on + if (channel->on) { + memcpy(&svision->ch[j], channel, sizeof(svision->ch[j])); + channel->on = 0; + } + } + } + } + + if (svision->m_noise.on && (svision->m_noise.play || svision->m_noise.count != 0)) { + s = (svision->m_noise.value * svision->m_noise.volume) << 2; + s = svision->ch_mask&1?s:0; + chout[3] = 0; + if (svision->m_noise.left) { + *left += s; + chout[3] = s; + } + if (svision->m_noise.right) { + *right += s; + chout[3] = s; + } + svision->m_noise.pos += svision->m_noise.step; + while (svision->m_noise.pos >= 1.0) { // if/while difference - Pacific Battle + // LFSR: x^2 + x + 1 + uint16 feedback; + svision->m_noise.value = svision->m_noise.state & 1; + feedback = ((svision->m_noise.state >> 1) ^ svision->m_noise.state) & 0x0001; + feedback <<= svision->m_noise.type; + svision->m_noise.state = (svision->m_noise.state >> 1) | feedback; + svision->m_noise.pos -= 1.0; + } + } + + chout[2] = 0; + if (svision->m_dma.on) { + uint8 sample; + uint16 addr = svision->m_dma.start + (uint16)svision->m_dma.pos / 2; + if (addr >= 0x8000 && addr < 0xc000) { + sample = svision->supervision_dma_mem[(addr & 0x3fff) | svision->m_dma.ca14to16]; + } + if (((uint16)svision->m_dma.pos) & 1) + s = (sample & 0xf); + else + s = (sample & 0xf0) >> 4; + s <<= 2; + s = ((svision->ch_mask>>1)&1)?s:0; + chout[2] = 0; + if (svision->m_dma.left) { + *left += s; + chout[2] = s; + } + if (svision->m_dma.right) { + *right += s; + chout[2] = s; + } + svision->m_dma.pos += svision->m_dma.step; + if (svision->m_dma.pos >= svision->m_dma.size) { + svision->m_dma.on = 0; + } + } + + if (svision->decrement_tick > SV_DEC_TICK) { + svision->decrement_tick = 0; + supervision_sound_decrement(svision); + } + svision->decrement_tick++; + } +} + +void supervision_sound_decrement(struct svision_t *svision) +{ + if (svision->m_channel[0].count > 0) + svision->m_channel[0].count--; + if (svision->m_channel[1].count > 0) + svision->m_channel[1].count--; + if (svision->m_noise.count > 0) + svision->m_noise.count--; +} + +void supervision_sound_wave_write(struct svision_t *svision, int which, int offset, uint8 data) +{ + SVISION_CHANNEL *channel = &svision->m_channel[which]; + + channel->reg[offset] = data; + switch (offset) { + case 0: + case 1: { + uint16 size; + size = channel->reg[0] | ((channel->reg[1] & 7) << 8); + // if size == 0 then channel->size == 0 + if (size) + channel->size = (uint16)(((real)SV_SAMPLE_RATE) * ((real)((size + 1) << 5)) / ((real)svision->UNSCALED_CLOCK)); + else + channel->size = 0; + channel->pos = 0; + // Popo Team + if (channel->count != 0 || svision->ch[which].size == 0 || channel->size == 0) { + svision->ch[which].size = channel->size; + if (channel->count == 0) + svision->ch[which].pos = 0; + } + } + break; + case 2: + channel->on = data & 0x40; + channel->waveform = (data & 0x30) >> 4; + channel->volume = data & 0x0f; + if (!channel->on || svision->ch[which].size == 0 || channel->size == 0) { + uint16 pos = svision->ch[which].pos; + memcpy(&svision->ch[which], channel, sizeof(svision->ch[which])); + if (channel->count != 0) // Journey to the West + svision->ch[which].pos = pos; + } + break; + case 3: + channel->count = data + 1; + svision->ch[which].size = channel->size; // Sonny Xpress! + break; + } +} + +void supervision_sound_dma_write(struct svision_t *svision, int offset, uint8 data) +{ + svision->m_dma.reg[offset] = data; + switch (offset) { + case 0: + case 1: + svision->m_dma.start = (svision->m_dma.reg[0] | (svision->m_dma.reg[1] << 8)); + break; + case 2: + svision->m_dma.size = (data ? data : 0x100) * 32; // Number of 4-bit samples + break; + case 3: + // Test games: Classic Casino, SSSnake + svision->m_dma.step = ((real)svision->UNSCALED_CLOCK) / ((real)SV_SAMPLE_RATE * (256 << (data & 3))); + // MESS/MAME. Wrong + //svision->m_dma.step = svision->UNSCALED_CLOCK / (256.0 * SV_SAMPLE_RATE * (1 + (data & 3))); + svision->m_dma.right = data & 4; + svision->m_dma.left = data & 8; + svision->m_dma.ca14to16 = ((data & 0x70) >> 4) << 14; + break; + case 4: + svision->m_dma.on = data & 0x80; + if (svision->m_dma.on) { + svision->m_dma.pos = 0.0; + } + break; + } +} + +void supervision_sound_noise_write(struct svision_t *svision, int offset, uint8 data) +{ + svision->m_noise.reg[offset] = data; + switch (offset) { + case 0: { + uint32 divisor = 8 << (data >> 4); + if (divisor) + svision->m_noise.step = ((real)svision->UNSCALED_CLOCK) / ((real)SV_SAMPLE_RATE * divisor); + else + svision->m_noise.step = 0; + + svision->m_noise.step = ((real)svision->UNSCALED_CLOCK) / ((real)SV_SAMPLE_RATE * divisor); + svision->m_noise.volume = data & 0xf; + } + break; + case 1: + svision->m_noise.count = data + 1; + break; + case 2: + svision->m_noise.type = (data & 1) ? 14 : 6; + svision->m_noise.play = data & 2; + svision->m_noise.right = data & 4; + svision->m_noise.left = data & 8; + svision->m_noise.on = data & 0x10; /* honey bee start */ + svision->m_noise.state = 1; + break; + } + svision->m_noise.pos = 0.0; +} diff --git a/src/engine/platform/sound/supervision.h b/src/engine/platform/sound/supervision.h new file mode 100644 index 000000000..0c3cfec00 --- /dev/null +++ b/src/engine/platform/sound/supervision.h @@ -0,0 +1,76 @@ +#ifndef __SUPERVISION_SOUND_H__ +#define __SUPERVISION_SOUND_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef float real; +typedef uint8_t BOOL; + + +typedef struct { + uint8 reg[4]; + int on; + uint8 waveform, volume; + uint16 pos, size; + uint16 count; +} SVISION_CHANNEL; + +typedef struct { + uint8 reg[3]; + int on, right, left, play; + uint8 type; // 6 - 7-Bit, 14 - 15-Bit + uint16 state; + uint8 value, volume; + uint16 count; + real pos, step; +} SVISION_NOISE; + +typedef struct { + uint8 reg[5]; + int on, right, left; + uint32 ca14to16; + uint16 start; + uint16 size; + real pos, step; +} SVISION_DMA; + +struct svision_t { + SVISION_CHANNEL m_channel[2]; + // For clear sound (no grating), sync with m_channel + SVISION_CHANNEL ch[2]; + SVISION_NOISE m_noise; + SVISION_DMA m_dma; + uint8 supervision_dma_mem[65536]; + uint32 decrement_tick; + uint32 UNSCALED_CLOCK; + uint8 ch_mask, flags; +}; + +void supervision_sound_reset(struct svision_t *svision); +void supervision_sound_set_clock(struct svision_t *svision, uint32 clock); +void supervision_sound_stream_update(struct svision_t *svision, uint8 *stream, uint32 len); +void supervision_sound_decrement(struct svision_t *svision); +void supervision_sound_wave_write(struct svision_t *svision, int which, int offset, uint8 data); +void supervision_sound_dma_write(struct svision_t *svision,int offset, uint8 data); +void supervision_sound_noise_write(struct svision_t *svision, int offset, uint8 data); +void supervision_sound_noise_write(struct svision_t *svision, int offset, uint8 data); +void supervision_memorymap_registers_write(struct svision_t *svision, uint32 Addr, uint8 Value); +// 12SN +void supervision_set_mute_mask(struct svision_t *svision, uint8 mask); +void supervision_sound_set_flags(struct svision_t *svision, uint8 flags_set); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif + diff --git a/src/engine/platform/sound/upd1771c.c b/src/engine/platform/sound/upd1771c.c new file mode 100644 index 000000000..0ed1c9f5e --- /dev/null +++ b/src/engine/platform/sound/upd1771c.c @@ -0,0 +1,300 @@ +// SOME CODE IS TAKEN FROM MAME'S EMULATION OF THE UPD1771C +// MADE BY AART1256 IN 2024 + +#include "upd1771c.h" + +#include + +/* + Each of the 8 waveforms have been extracted from the uPD1771c-017 internal + ROM, from offset 0x1fd (start of first waveform) to offset 0x2fc (end of + last waveform). + (note: given test mode dumping offset non-clarity it may be 0x200-0x2ff) + The waveforms are stored in an 8-bit sign-magnitude format, so if in ROM the + upper bit is 0x80, invert the lower 7 bits to get the 2's complement result + seen here. + Note that only the last 4 waveforms appear to have been intended for use as + waveforms; the first four look as if they're playing back a piece of code as + wave data. +*/ +const signed char WAVEFORMS[8][32]={ +{ 0, 0,-123,-123, -61, -23, 125, 107, 94, 83,-128,-128,-128, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,-128,-128,-128, 0, 0, 0, 0, 0, 0}, +{ 37, 16, 32, -21, 32, 52, 4, 4, 33, 18, 60, 56, 0, 8, 5, 16, 65, 19, 69, 16, -2, 19, 37, 16, 97, 19, 0, 87, 127, -3, 1, 2}, +{ 0, 8, 1, 52, 4, 0, 0, 77, 81,-109, 47, 97, -83,-109, 38, 97, 0, 52, 4, 0, 1, 4, 1, 22, 2, -46, 33, 97, 0, 8, -85, -99}, +{ 47, 97, 40, 97, -3, 25, 64, 17, 0, 52, 12, 5, 12, 5, 12, 5, 12, 5, 12, 5, 8, 4,-114, 19, 0, 52,-122, 21, 2, 5, 0, 8}, +{ -52, -96,-118,-128,-111, -74, -37, -5, 31, 62, 89, 112, 127, 125, 115, 93, 57, 23, 0, -16, -8, 15, 37, 54, 65, 70, 62, 54, 43, 31, 19, 0}, +{ -81,-128, -61, 13, 65, 93, 127, 47, 41, 44, 52, 55, 56, 58, 58, 34, 0, 68, 76, 72, 61, 108, 55, 29, 32, 39, 43, 49, 50, 51, 51, 0}, +{ -21, -45, -67, -88,-105,-114,-122,-128,-123,-116,-103, -87, -70, -53, -28, -9, 22, 46, 67, 86, 102, 114, 123, 125, 127, 117, 104, 91, 72, 51, 28, 0}, +{ -78,-118,-128,-102, -54, -3, 40, 65, 84, 88, 84, 80, 82, 88, 94, 103, 110, 119, 122, 125, 122, 122, 121, 123, 125, 126, 127, 127, 125, 118, 82, 0} +}; + + +#define NOISE_SIZE 255 + + +/* +const unsigned char noise_tbl[]= +{ + 0x1c,0x86,0x8a,0x8f,0x98,0xa1,0xad,0xbe,0xd9,0x8a,0x66,0x4d,0x40,0x33,0x2b,0x23, + 0x1e,0x8a,0x90,0x97,0xa4,0xae,0xb8,0xd6,0xec,0xe9,0x69,0x4a,0x3e,0x34,0x2d,0x27, + 0x24,0x24,0x89,0x8e,0x93,0x9c,0xa5,0xb0,0xc1,0xdd,0x40,0x36,0x30,0x29,0x27,0x24, + 0x8b,0x90,0x96,0x9e,0xa7,0xb3,0xc4,0xe1,0x25,0x21,0x8a,0x8f,0x93,0x9d,0xa5,0xb2, + 0xc2,0xdd,0xdd,0x98,0xa2,0xaf,0xbf,0xd8,0xfd,0x65,0x4a,0x3c,0x31,0x2b,0x24,0x22, + 0x1e,0x87,0x8c,0x91,0x9a,0xa3,0xaf,0xc0,0xdb,0xbe,0xd9,0x8c,0x66,0x4d,0x40,0x34, + 0x2c,0x24,0x1f,0x88,0x90,0x9a,0xa4,0xb2,0xc2,0xda,0xff,0x67,0x4d,0x3d,0x34,0x2d, + 0x26,0x24,0x20,0x89,0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xde,0xc1,0xda,0xff,0x67,0x4d, + 0x3d,0x33,0x2d,0x26,0x24,0x20,0x89,0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xdd,0xa3,0xb0, + 0xc0,0xd9,0xfe,0x66,0x4b,0x3c,0x32,0x2b,0x24,0x23,0x1e,0x88,0x8d,0x92,0x9b,0xa4, + 0xb0,0xc1,0xdc,0xad,0xbe,0xda,0x22,0x20,0x1c,0x85,0x8a,0x8f,0x98,0xa1,0xad,0xbe, + 0xda,0x20,0x1b,0x85,0x8d,0x97,0xa1,0xaf,0xbf,0xd8,0xfd,0x64,0x49,0x3a,0x30,0x2a, + 0x23,0x21,0x1d,0x86,0x8b,0x91,0x9a,0xa2,0xae,0xc0,0xdb,0x33,0x2b,0x24,0x1f,0x88, + 0x90,0x9a,0xa4,0xb2,0xc2,0xda,0xff,0x67,0x4c,0x3e,0x33,0x2d,0x25,0x24,0x1f,0x89, + 0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xde,0x85,0x8e,0x98,0xa2,0xb0,0xc0,0xd9,0xfe,0x64, + 0x4b,0x3b,0x31,0x2a,0x23,0x22,0x1e,0x88,0x8c,0x91,0x9b,0xa3,0xaf,0xc1,0xdc,0xdc +}; +*/ + +const unsigned char noise_tbl[8][256] = { +{ + 0x84, 0x87, 0x8c, 0x93, 0x9a, 0xa4, 0xb2, 0xc8, 0x8a, 0x6c, 0x58, 0x4e, 0x43, 0x3d, 0x37, 0x33, + 0x86, 0x8d, 0x95, 0x9d, 0xa8, 0xb5, 0xc9, 0xe7, 0x6d, 0x58, 0x4b, 0x43, 0x3e, 0x38, 0x37, 0x34, + 0x87, 0x8b, 0x8f, 0x96, 0x9d, 0xa7, 0xb5, 0xcb, 0x4d, 0x45, 0x40, 0x3a, 0x39, 0x35, 0x89, 0x8d, + 0x91, 0x98, 0x9f, 0xa9, 0xb7, 0xcd, 0x37, 0x34, 0x87, 0x8c, 0x90, 0x97, 0x9e, 0xa8, 0xb5, 0xcb, + 0xca, 0x93, 0x9b, 0xa6, 0xb3, 0xc7, 0xe5, 0x6b, 0x56, 0x49, 0x41, 0x3c, 0x36, 0x35, 0x32, 0x86, + 0x89, 0x8e, 0x95, 0x9c, 0xa6, 0xb4, 0xc9, 0xb2, 0xc8, 0x8a, 0x6c, 0x58, 0x4d, 0x43, 0x3d, 0x37, + 0x33, 0x86, 0x8d, 0x95, 0x9d, 0xa8, 0xb5, 0xc9, 0xe7, 0x6d, 0x58, 0x4b, 0x43, 0x3e, 0x38, 0x37, + 0x34, 0x87, 0x8b, 0x8f, 0x97, 0x9e, 0xa8, 0xb5, 0xcb, 0xb5, 0xc9, 0xe6, 0x6d, 0x58, 0x4b, 0x43, + 0x3d, 0x38, 0x37, 0x33, 0x87, 0x8b, 0x8f, 0x96, 0x9e, 0xa7, 0xb5, 0xcb, 0x9c, 0xa7, 0xb3, 0xc8, + 0xe6, 0x6c, 0x56, 0x4a, 0x42, 0x3d, 0x37, 0x36, 0x33, 0x86, 0x8a, 0x8e, 0x95, 0x9c, 0xa6, 0xb4, + 0xca, 0xa4, 0xb2, 0xc8, 0x35, 0x33, 0x30, 0x84, 0x88, 0x8c, 0x93, 0x9a, 0xa5, 0xb2, 0xc8, 0x34, + 0x30, 0x84, 0x8a, 0x93, 0x9a, 0xa5, 0xb2, 0xc6, 0xe4, 0x6a, 0x55, 0x49, 0x41, 0x3b, 0x36, 0x34, + 0x31, 0x85, 0x89, 0x8d, 0x94, 0x9b, 0xa5, 0xb3, 0xc9, 0x43, 0x3d, 0x37, 0x33, 0x86, 0x8d, 0x95, + 0x9d, 0xa8, 0xb4, 0xc9, 0xe7, 0x6d, 0x57, 0x4b, 0x43, 0x3e, 0x38, 0x37, 0x33, 0x87, 0x8b, 0x8f, + 0x96, 0x9d, 0xa7, 0xb5, 0xcb, 0x85, 0x8b, 0x93, 0x9b, 0xa6, 0xb3, 0xc7, 0xe5, 0x6b, 0x56, 0x4a, + 0x41, 0x3c, 0x36, 0x35, 0x32, 0x86, 0x89, 0x8e, 0x95, 0x9c, 0xa6, 0xb4, 0xca, 0x30 +}, + +{ + 0x95, 0x95, 0x98, 0x9c, 0x9e, 0xa3, 0xaa, 0xb4, 0xb3, 0x98, 0x9c, 0xa1, 0xa7, 0xb1, 0xc0, 0x57, + 0x4f, 0x49, 0x48, 0x44, 0x42, 0x40, 0x40, 0x94, 0x95, 0x96, 0x9a, 0x9d, 0xa2, 0xa9, 0xb3, 0xa8, + 0xb1, 0x66, 0x59, 0x50, 0x4c, 0x4a, 0x46, 0x44, 0x41, 0x94, 0x97, 0x9b, 0x9e, 0xa4, 0xaa, 0xb3, + 0xc2, 0x59, 0x52, 0x4c, 0x4a, 0x46, 0x45, 0x43, 0x43, 0x96, 0x97, 0x99, 0x9d, 0x9f, 0xa5, 0xab, + 0xb5, 0xaa, 0xb4, 0xc2, 0x5a, 0x52, 0x4c, 0x4b, 0x47, 0x45, 0x43, 0x43, 0x96, 0x97, 0x99, 0x9d, + 0xa0, 0xa5, 0xab, 0xb5, 0x9d, 0xa3, 0xa9, 0xb3, 0xc2, 0x59, 0x51, 0x4b, 0x4a, 0x46, 0x44, 0x42, + 0x42, 0x96, 0x97, 0x98, 0x9c, 0x9f, 0xa4, 0xab, 0xb4, 0xa2, 0xa9, 0xb3, 0x41, 0x40, 0x40, 0x93, + 0x94, 0x96, 0x9a, 0x9d, 0xa2, 0xa8, 0xb2, 0x41, 0x3e, 0x92, 0x94, 0x99, 0x9b, 0xa2, 0xa7, 0xb1, + 0xc0, 0x57, 0x4f, 0x4a, 0x48, 0x44, 0x42, 0x40, 0x40, 0x94, 0x95, 0x97, 0x9a, 0x9d, 0xa2, 0xa9, + 0xb3, 0x48, 0x45, 0x43, 0x41, 0x94, 0x97, 0x9b, 0x9d, 0xa3, 0xa9, 0xb3, 0xc1, 0x59, 0x51, 0x4b, + 0x4a, 0x45, 0x44, 0x42, 0x42, 0x95, 0x97, 0x98, 0x9c, 0x9f, 0xa4, 0xab, 0xb4, 0x92, 0x95, 0x99, + 0x9c, 0xa2, 0xa8, 0xb1, 0xc0, 0x57, 0x4f, 0x4a, 0x48, 0x44, 0x42, 0x41, 0x41, 0x94, 0x95, 0x97, + 0x9b, 0x9e, 0xa3, 0xa9, 0xb3, 0x3e, 0x92, 0x93, 0x95, 0x98, 0x9b, 0xa0, 0xa7, 0xb1, 0x65, 0x58, + 0x50, 0x4b, 0x49, 0x45, 0x43, 0x41, 0x94, 0x97, 0x9b, 0x9e, 0xa4, 0xa9, 0xb3, 0xc2, 0x59, 0x51, + 0x4b, 0x4a, 0x46, 0x45, 0x42, 0x42, 0x96, 0x96, 0x99, 0x9c, 0x9f, 0xa4, 0xab, 0xb5, 0x4d, 0x4b, + 0x47, 0x45, 0x43, 0x43, 0x97, 0x97, 0x9a, 0x9d, 0xa0, 0xa5, 0xac, 0xb6, 0x41, 0x42 + +}, + +{ + 0x93, 0x93, 0x93, 0x94, 0x95, 0x99, 0x9c, 0xa1, 0xa8, 0x4e, 0x48, 0x45, 0x43, 0x44, 0x42, 0x42, + 0x95, 0x95, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9e, 0xa6, 0x43, 0x41, 0x41, 0x95, 0x94, 0x94, 0x95, + 0x97, 0x9b, 0x9d, 0xa3, 0xa9, 0x4f, 0x4a, 0x46, 0x45, 0x45, 0x43, 0x43, 0x97, 0x96, 0x96, 0x97, + 0x99, 0x9a, 0x9c, 0x9f, 0xa7, 0x94, 0x93, 0x94, 0x96, 0x9a, 0x9d, 0xa2, 0xa9, 0x4f, 0x49, 0x45, + 0x44, 0x45, 0x43, 0x43, 0x96, 0x96, 0x96, 0x96, 0x98, 0x99, 0x9c, 0x9f, 0xa7, 0x93, 0x93, 0x93, + 0x93, 0x95, 0x96, 0x99, 0x9c, 0xa4, 0x50, 0x4b, 0x49, 0x45, 0x44, 0x42, 0x42, 0x95, 0x95, 0x95, + 0x96, 0x97, 0x9b, 0x9e, 0xa3, 0xaa, 0x50, 0x4b, 0x46, 0x46, 0x46, 0x44, 0x44, 0x97, 0x97, 0x97, + 0x97, 0x99, 0x9a, 0x9d, 0x9f, 0xa8, 0x47, 0x46, 0x46, 0x44, 0x44, 0x98, 0x98, 0x97, 0x98, 0x9a, + 0x9b, 0x9d, 0xa0, 0xa8, 0x42, 0x96, 0x95, 0x95, 0x96, 0x98, 0x98, 0x9b, 0x9e, 0xa6, 0xa6, 0x93, + 0x95, 0x99, 0x9c, 0xa1, 0xa7, 0x4e, 0x48, 0x44, 0x43, 0x44, 0x41, 0x42, 0x95, 0x95, 0x95, 0x95, + 0x97, 0x98, 0x9b, 0x9e, 0xa6, 0x9d, 0xa5, 0x52, 0x4c, 0x4b, 0x46, 0x45, 0x42, 0x43, 0x97, 0x96, + 0x96, 0x97, 0x98, 0x9c, 0x9f, 0xa4, 0xab, 0x51, 0x4b, 0x48, 0x46, 0x47, 0x44, 0x45, 0x98, 0x98, + 0x98, 0x98, 0x9a, 0x9b, 0x9e, 0xa0, 0xa8, 0x9f, 0xa4, 0xab, 0x51, 0x4c, 0x48, 0x47, 0x47, 0x45, + 0x45, 0x99, 0x98, 0x98, 0x99, 0x9a, 0x9b, 0x9e, 0xa1, 0xa9, 0x98, 0x9d, 0x9f, 0xa4, 0xab, 0x51, + 0x4b, 0x47, 0x47, 0x47, 0x45, 0x45, 0x98, 0x98, 0x98, 0x98, 0x9a, 0x9b, 0x9e, 0xa1, 0xa8, 0x9c, + 0x9f, 0xa7, 0x42, 0x42, 0x95, 0x95, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9e, 0xa6, 0x3f + +}, + +{ + 0x94, 0x93, 0x92, 0x92, 0x93, 0x95, 0x96, 0x99, 0x9c, 0x43, 0x43, 0x43, 0x41, 0x41, 0x42, 0x95, + 0x95, 0x95, 0x94, 0x94, 0x94, 0x95, 0x97, 0x97, 0x9a, 0x40, 0x40, 0x41, 0x95, 0x95, 0x94, 0x95, + 0x95, 0x97, 0x97, 0x9a, 0x9e, 0x45, 0x44, 0x44, 0x42, 0x42, 0x43, 0x97, 0x96, 0x96, 0x96, 0x96, + 0x95, 0x96, 0x98, 0x99, 0x9c, 0x94, 0x94, 0x94, 0x94, 0x96, 0x97, 0x9a, 0x9d, 0x44, 0x43, 0x44, + 0x42, 0x42, 0x43, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x96, 0x98, 0x98, 0x9b, 0x93, 0x93, 0x93, + 0x92, 0x92, 0x93, 0x95, 0x95, 0x98, 0x49, 0x44, 0x43, 0x44, 0x42, 0x42, 0x43, 0x96, 0x96, 0x96, + 0x95, 0x96, 0x98, 0x99, 0x9b, 0x9f, 0x46, 0x45, 0x45, 0x43, 0x44, 0x44, 0x98, 0x98, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x99, 0x9a, 0x9d, 0x45, 0x43, 0x43, 0x44, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, + 0x97, 0x99, 0x9a, 0x9d, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x96, 0x97, 0x9a, 0x9a, 0x92, + 0x93, 0x94, 0x95, 0x98, 0x9c, 0x43, 0x42, 0x43, 0x40, 0x40, 0x41, 0x95, 0x95, 0x94, 0x94, 0x94, + 0x93, 0x94, 0x96, 0x97, 0x9a, 0x96, 0x99, 0x4a, 0x45, 0x44, 0x44, 0x42, 0x42, 0x43, 0x97, 0x96, + 0x96, 0x96, 0x97, 0x98, 0x99, 0x9c, 0xa0, 0x47, 0x46, 0x46, 0x44, 0x44, 0x45, 0x98, 0x98, 0x98, + 0x98, 0x97, 0x97, 0x98, 0x99, 0x9a, 0x9d, 0x9a, 0x9c, 0xa0, 0x47, 0x46, 0x46, 0x44, 0x44, 0x45, + 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x98, 0x9a, 0x9a, 0x9e, 0x97, 0x98, 0x99, 0x9c, 0xa0, 0x47, + 0x46, 0x46, 0x44, 0x44, 0x45, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97, 0x98, 0x9a, 0x9a, 0x9d, 0x98, + 0x99, 0x9c, 0x42, 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x95, 0x96, 0x97, 0x9a, 0x40 + +}, + +{ + 0xe6, 0x1c, 0x87, 0xcb, 0x42, 0x9b, 0x87, 0x35, 0xd8, 0x1c, 0xa3, 0x58, 0x84, 0xa4, 0x28, 0x71, + 0xd9, 0x3e, 0x82, 0xbb, 0x28, 0xd2, 0x80, 0x63, 0xe0, 0x38, 0x71, 0xcb, 0x22, 0x81, 0x9b, 0x83, + 0xa4, 0x28, 0x71, 0xd8, 0x3e, 0x81, 0xbb, 0x28, 0xd2, 0x80, 0x63, 0xe0, 0x37, 0x71, 0xcb, 0x22, + 0x81, 0x9b, 0xa3, 0x57, 0x83, 0xa3, 0x28, 0x70, 0xd8, 0x3e, 0x81, 0xba, 0x28, 0xd1, 0x80, 0x63, + 0xe0, 0x37, 0x70, 0xcb, 0x22, 0x81, 0x9b, 0x23, 0x82, 0x9b, 0x28, 0xd2, 0x80, 0x63, 0xe1, 0x38, + 0x71, 0xcc, 0x23, 0x81, 0x9b, 0x9c, 0x88, 0x36, 0xd9, 0x1d, 0xa4, 0x58, 0x84, 0xa5, 0x28, 0x72, + 0xd9, 0x3f, 0x82, 0xbb, 0x29, 0xd2, 0x81, 0x64, 0xe1, 0x38, 0x72, 0xcc, 0x23, 0x82, 0x9b, 0xcc, + 0x43, 0x9c, 0x88, 0x36, 0xd8, 0x1d, 0xa4, 0x58, 0x84, 0xa5, 0x29, 0x71, 0xd9, 0x3f, 0x82, 0xbc, + 0x29, 0xd2, 0x81, 0x64, 0xe1, 0x39, 0x72, 0xcc, 0x23, 0x82, 0x9c, 0x37, 0xd9, 0x1d, 0xa4, 0x59, + 0x85, 0xa6, 0x29, 0x72, 0xda, 0x40, 0x83, 0xbc, 0x2a, 0xd3, 0x82, 0x64, 0xe2, 0x39, 0x72, 0xcc, + 0x24, 0x82, 0x9c, 0x82, 0x64, 0xe1, 0x3a, 0x72, 0xcc, 0x24, 0x82, 0x9c, 0x40, 0xe8, 0x1e, 0x89, + 0xcd, 0x44, 0x9d, 0x89, 0x37, 0xd9, 0x1e, 0xa5, 0x59, 0x85, 0xa6, 0x2a, 0x73, 0xda, 0x40, 0x84, + 0xbd, 0x2a, 0xd3, 0x82, 0x65, 0xe2, 0x39, 0x73, 0xcd, 0x24, 0x83, 0x9c, 0x40, 0x83, 0xbd, 0x2a, + 0xd3, 0x82, 0x65, 0xe2, 0x39, 0x73, 0xcd, 0x24, 0x83, 0x9c, 0xd2, 0x81, 0x63, 0xe0, 0x39, 0x71, + 0xcc, 0x23, 0x82, 0x9b, 0x9b, 0x1d, 0xa4, 0x59, 0x84, 0xa5, 0x29, 0x72, 0xd9, 0x3f, 0x83, 0xbc, + 0x29, 0xd3, 0x81, 0x64, 0xe1, 0x39, 0x72, 0xcc, 0x24, 0x82, 0x9c, 0x82, 0x9c, 0x3f + +}, + +{ + 0xe7, 0x89, 0x1b, 0x8b, 0xe6, 0x89, 0x1b, 0x8b, 0xe6, 0x89, 0x1b, 0x8a, 0xe6, 0x88, 0x1b, 0x8a, + 0xe6, 0x88, 0x1a, 0x8a, 0x1c, 0x8b, 0xe7, 0x89, 0x1b, 0x8b, 0xe6, 0x89, 0x1b, 0x8a, 0xe6, 0x89, + 0x1b, 0x8a, 0xe6, 0x89, 0x1b, 0x8a, 0xe6, 0x89, 0x1a, 0x8a, 0xe6, 0x88, 0x1a, 0x89, 0xe5, 0x88, + 0x1a, 0x89, 0xe5, 0x88, 0x1a, 0xe5, 0x88, 0x1a, 0x89, 0xe5, 0x88, 0x1a, 0x89, 0xe4, 0x87, 0x19, + 0x89, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x1a, 0x8a, 0xe5, 0x88, 0x1a, 0x89, 0xe5, 0x88, + 0x1a, 0x89, 0xe5, 0x88, 0x1a, 0x89, 0xe4, 0x87, 0x19, 0x89, 0xe4, 0x87, 0x19, 0xe4, 0x87, 0x19, + 0xe4, 0x87, 0x1a, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0x1a, 0x89, 0xe5, 0x88, + 0x1a, 0x89, 0xe5, 0x88, 0x1a, 0x89, 0xe5, 0x87, 0x1a, 0x89, 0xe4, 0x88, 0x19, 0x89, 0xe4, 0x87, + 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, + 0x19, 0x88, 0xe3, 0x86, 0x19, 0x88, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x88, 0xe3, 0x86, + 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, + 0x18, 0x87, 0xe2, 0x85, 0x18, 0x87, 0xe3, 0x85, 0x17, 0x19, 0x88, 0xe3, 0x86, 0x19, 0x88, 0xe4, + 0x86, 0x19, 0x88, 0xe3, 0x86, 0x18, 0x88, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, + 0x86, 0x18, 0x87, 0xe3, 0x85, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, + 0x85, 0x18, 0x87, 0x19, 0x88, 0xe3, 0x86, 0x19, 0x88, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, + 0x87, 0x1a, 0x89, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x1b, 0x89, 0x1c, 0x8b + +}, + +{ + 0xa1, 0xe6, 0xcf, 0x79, 0x58, 0x4a, 0x78, 0x7a, 0x53, 0x7b, 0xc2, 0xc3, 0x82, 0x36, 0x48, 0x84, + 0xcb, 0xa6, 0x67, 0x2e, 0x49, 0x7c, 0xaa, 0x93, 0x87, 0x73, 0x62, 0x6c, 0xa4, 0xaa, 0x71, 0xa7, + 0x67, 0x2f, 0x4a, 0x7c, 0xab, 0x94, 0x87, 0x74, 0x62, 0x6d, 0xa4, 0xab, 0x72, 0x7b, 0xaa, 0x93, + 0x87, 0x72, 0x61, 0x6c, 0xa4, 0xaa, 0x71, 0x72, 0x7b, 0xc2, 0xc3, 0x81, 0x36, 0x48, 0x84, 0xcb, + 0xa6, 0x67, 0x2e, 0x49, 0x7b, 0xaa, 0x93, 0x87, 0x72, 0x61, 0x6c, 0xa4, 0xaa, 0x71, 0xaa, 0x71, + 0x59, 0xa0, 0xe5, 0xce, 0x78, 0x57, 0x49, 0x77, 0x79, 0x52, 0x7a, 0xc1, 0xc2, 0x81, 0x35, 0x47, + 0x83, 0xca, 0xa6, 0x66, 0x2d, 0x48, 0x7b, 0xa9, 0x92, 0x86, 0x72, 0x61, 0x6b, 0xa3, 0xa9, 0x70, + 0x82, 0x36, 0x49, 0x84, 0xcc, 0xa7, 0x67, 0x2e, 0x4a, 0x7c, 0xab, 0x94, 0x87, 0x73, 0x62, 0x6c, + 0xa4, 0xab, 0x72, 0xc2, 0xc3, 0x82, 0x36, 0x48, 0x84, 0xcb, 0xa6, 0x67, 0x2e, 0x4a, 0x7c, 0xaa, + 0x93, 0x87, 0x73, 0x62, 0x6c, 0xa4, 0xaa, 0x71, 0xa3, 0xaa, 0x70, 0x49, 0x7b, 0xa9, 0x92, 0x86, + 0x72, 0x61, 0x6b, 0xa3, 0xaa, 0x70, 0x4b, 0x78, 0x7a, 0x53, 0x7c, 0xc2, 0xc3, 0x82, 0x36, 0x49, + 0x84, 0xcc, 0xa6, 0x67, 0x2f, 0x4a, 0x7c, 0xaa, 0x93, 0x87, 0x73, 0x62, 0x6c, 0xa4, 0xab, 0x72, + 0x7b, 0x5a, 0x4c, 0x7a, 0x7c, 0x55, 0x7e, 0xc4, 0xc5, 0x83, 0x38, 0x4a, 0x85, 0xcd, 0xa8, 0x69, + 0x30, 0x4b, 0x7d, 0xac, 0x95, 0x88, 0x74, 0x63, 0x6e, 0xa5, 0xac, 0x73, 0x7c, 0x55, 0x7d, 0xc4, + 0xc5, 0x83, 0x37, 0x4a, 0x85, 0xcd, 0xa8, 0x68, 0x30, 0x4b, 0x7d, 0xac, 0x95, 0x88, 0x74, 0x63, + 0x6e, 0xa6, 0xac, 0x73, 0xab, 0x94, 0x87, 0x73, 0x62, 0x6d, 0xa5, 0xab, 0x72, 0x5a + +}, + +{ + 0xbf, 0xdc, 0xd4, 0xaa, 0x82, 0x81, 0x35, 0x34, 0x4d, 0x75, 0x95, 0xcb, 0xde, 0xc9, 0x91, 0x72, + 0x4a, 0x30, 0x6b, 0x57, 0x69, 0x7d, 0x65, 0x3f, 0x33, 0x4f, 0x69, 0x81, 0xc1, 0xde, 0xd6, 0xac, + 0x83, 0x82, 0x37, 0x36, 0x4f, 0x77, 0x96, 0xcd, 0xe0, 0xca, 0x93, 0x73, 0x4c, 0x31, 0x6d, 0x58, + 0x6a, 0x4f, 0x68, 0x80, 0xc0, 0xdd, 0xd5, 0xab, 0x82, 0x82, 0x36, 0x35, 0x4e, 0x76, 0x95, 0xcc, + 0xdf, 0xca, 0x92, 0x73, 0x4b, 0x31, 0x6c, 0x57, 0x6a, 0xe0, 0xca, 0x92, 0x73, 0x4c, 0x31, 0x6c, + 0x58, 0x6a, 0xad, 0xd5, 0xdb, 0xbc, 0x7b, 0x63, 0x3c, 0x31, 0x4e, 0x66, 0x7e, 0xbe, 0xdb, 0xd3, + 0xaa, 0x81, 0x80, 0x34, 0x33, 0x4c, 0x75, 0x94, 0xcb, 0xde, 0xc8, 0x91, 0x71, 0x4a, 0x2f, 0x6b, + 0x56, 0x68, 0x35, 0x4e, 0x76, 0x95, 0xcc, 0xdf, 0xca, 0x92, 0x72, 0x4b, 0x30, 0x6c, 0x57, 0x6a, + 0xcc, 0xdf, 0xca, 0x91, 0x72, 0x4b, 0x30, 0x6c, 0x57, 0x69, 0x69, 0x7e, 0xbe, 0xdb, 0xd3, 0xa9, + 0x81, 0x80, 0x34, 0x33, 0x4c, 0x74, 0x94, 0xcb, 0xdd, 0xc8, 0x90, 0x71, 0x4a, 0x2f, 0x6b, 0x55, + 0x68, 0x56, 0x69, 0xac, 0xd4, 0xd9, 0xbb, 0x7a, 0x62, 0x3c, 0x30, 0x4c, 0x65, 0x7d, 0xbd, 0xda, + 0xd2, 0xa8, 0x80, 0x7f, 0x34, 0x33, 0x4c, 0x74, 0x94, 0xca, 0xdd, 0xc8, 0x90, 0x71, 0x49, 0x2e, + 0x6a, 0x55, 0x67, 0xd3, 0xa9, 0x81, 0x80, 0x34, 0x33, 0x4c, 0x74, 0x94, 0xca, 0xde, 0xc8, 0x91, + 0x71, 0x4a, 0x2f, 0x6a, 0x56, 0x68, 0xbd, 0xda, 0xd2, 0xa8, 0x7f, 0x7e, 0x33, 0x32, 0x4b, 0x73, + 0x92, 0xc9, 0xdc, 0xc7, 0x8f, 0x70, 0x49, 0x2e, 0x69, 0x54, 0x67, 0x6a, 0x56, 0x68, 0x93, 0xca, + 0xdd, 0xc8, 0x90, 0x70, 0x49, 0x2e, 0x6a, 0x55, 0x68, 0x3d, 0x31, 0x4e, 0x67, 0x7f + +}, + +}; + +void upd1771c_reset(struct upd1771c_t *scv) { + memset(scv->upd1771c_packets,0,16); + scv->upd1771c_mode = 0; + scv->upd1771c_pos = 0; + scv->upd1771c_posc = 0; + scv->upd1771c_wave = 0; + scv->upd1771c_vol = 0; + scv->upd1771c_period = 0; + scv->upd1771c_off = 0; + scv->upd1771c_npos = 0; + //scv->upd1771c_repsamp = 0; +} + +void upd1771c_write_packet(struct upd1771c_t *scv, uint8_t ind, uint8_t val) { + scv->upd1771c_packets[ind&15] = val; + switch (scv->upd1771c_packets[0]) { + case 1: + if (ind == 3) { + scv->upd1771c_mode = 1; + scv->upd1771c_wave = (scv->upd1771c_packets[1]&0xe0)>>5; + scv->upd1771c_off = 0; //? + scv->upd1771c_period = scv->upd1771c_packets[2]; + scv->upd1771c_vol = scv->upd1771c_packets[3]&0x1f; + } + break; + case 2: + if (ind == 3) { + scv->upd1771c_mode = 2; + scv->upd1771c_wave = (scv->upd1771c_packets[1]&0xe0)>>5; + scv->upd1771c_off = scv->upd1771c_packets[1]&0x1f; + scv->upd1771c_period = scv->upd1771c_packets[2]<0x20?0x20:scv->upd1771c_packets[2]; + scv->upd1771c_vol = scv->upd1771c_packets[3]&0x1f; + } + break; + default: + case 0: + scv->upd1771c_mode = 0; + break; + } +} + +void upd1771c_sound_set_clock(struct upd1771c_t *scv, unsigned int clock, unsigned int divi) { + scv->upd1771c_repsamp = divi; +} + +int16_t upd1771c_sound_stream_update(struct upd1771c_t *scv) { + int16_t s = 0; + for (int i = 0; i < scv->upd1771c_repsamp; i++) { + s = 0; + switch (scv->upd1771c_mode) { + case 2: + s = ((int16_t)WAVEFORMS[scv->upd1771c_wave][scv->upd1771c_posc])*scv->upd1771c_vol; + scv->upd1771c_pos++; + if (scv->upd1771c_pos >= scv->upd1771c_period) { + scv->upd1771c_pos=0; + scv->upd1771c_posc++; + if (scv->upd1771c_posc == 32) + scv->upd1771c_posc = scv->upd1771c_off; + } + break; + case 1: + scv->upd1771c_pos++; + if (scv->upd1771c_pos >= ((((uint32_t)scv->upd1771c_period) + 1)*128)) { + scv->upd1771c_pos=0; + scv->upd1771c_posc++; + if (scv->upd1771c_posc == NOISE_SIZE) + scv->upd1771c_posc = 0; + } + uint16_t p = scv->upd1771c_posc; + p = p>=254?253:p; + s = ((int16_t)(noise_tbl[scv->upd1771c_wave][p])-127)*scv->upd1771c_vol; + // inaccurate noise mixing :/ + // s |= (scv->upd1771c_npos&128)?127*scv->upd1771c_vol:0; + break; + case 0: + default: + break; + } + } + return s; +} + diff --git a/src/engine/platform/sound/upd1771c.h b/src/engine/platform/sound/upd1771c.h new file mode 100644 index 000000000..d0ea80f78 --- /dev/null +++ b/src/engine/platform/sound/upd1771c.h @@ -0,0 +1,33 @@ +#ifndef __UPD1771C_SOUND_H__ +#define __UPD1771C_SOUND_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +struct upd1771c_t { + uint8_t upd1771c_packets[16]; + uint8_t upd1771c_mode; + uint32_t upd1771c_pos; + uint8_t upd1771c_off; + uint8_t upd1771c_posc; + uint8_t upd1771c_wave; + uint8_t upd1771c_vol; + uint8_t upd1771c_period; + uint8_t upd1771c_npos; + int upd1771c_repsamp; +}; + +void upd1771c_reset(struct upd1771c_t *scv); +void upd1771c_write_packet(struct upd1771c_t *scv, uint8_t ind, uint8_t val); +int16_t upd1771c_sound_stream_update(struct upd1771c_t *scv); +void upd1771c_sound_set_clock(struct upd1771c_t *scv, unsigned int clock, unsigned int divi); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/src/engine/platform/supervision.cpp b/src/engine/platform/supervision.cpp new file mode 100644 index 000000000..45439ec4f --- /dev/null +++ b/src/engine/platform/supervision.cpp @@ -0,0 +1,599 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "supervision.h" +#include "../engine.h" +#include "../../ta-log.h" +#include "furIcons.h" +#include + +//#define rWrite(a,v) pendingWrites[a]=v; +#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} } + +#define CHIP_DIVIDER 32 + +const char* regCheatSheetSupervision[]={ + "Freq0L", "10", + "Freq0H", "11", + "Control0", "12", + "Len0", "13", + + "Freq1L", "14", + "Freq1H", "15", + "Control1", "16", + "Len1", "17", + + "DMAstartL", "18", + "DMAstartH", "19", + "DMAlen", "1a", + "DMAstep", "1b", + "DMAon", "1c", + + "NoisDiv", "28", + "NoisLen", "29", + "NoisCtrl", "2a", + + NULL +}; + +const char** DivPlatformSupervision::getRegisterSheet() { + return regCheatSheetSupervision; +} + +void DivPlatformSupervision::acquire(short** buf, size_t len) { + int mask_bits=0; + for (int i=0; i<4; i++) + mask_bits |= isMuted[i]?0:8>>i; + supervision_set_mute_mask(&svision,mask_bits); + + for (size_t h=0; hdata[oscBuf[i]->needle++]=CLAMP((((int)s[2+i])-128)*256,-32768,32767); + } + + tempL[0]=(tempL[0]>>1)+(tempL[0]>>2); + tempR[0]=(tempR[0]>>1)+(tempR[0]>>2); + + if (tempL[0]<-32768) tempL[0]=-32768; + if (tempL[0]>32767) tempL[0]=32767; + if (tempR[0]<-32768) tempR[0]=-32768; + if (tempR[0]>32767) tempR[0]=32767; + + //printf("tempL: %d tempR: %d\n",tempL,tempR); + buf[0][h]=tempL[0]; + buf[1][h]=tempR[0]; + } +} + +void DivPlatformSupervision::tick(bool sysTick) { + for (int i=0; i<4; i++) { + + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); + } + if (NEW_ARP_STRAT) { + chan[i].handleArp(); + } else if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + int f=parent->calcArp(chan[i].note,chan[i].std.arp.val); + if (i==2 || i==3) { + chan[i].baseFreq=f; + //if (chan[i].baseFreq>255) chan[i].baseFreq=255; + if (chan[i].baseFreq<0) chan[i].baseFreq=0; + } else { + chan[i].baseFreq=NOTE_PERIODIC(f); + } + } + chan[i].freqChanged=true; + } + if (chan[i].std.duty.had) { + chan[i].duty=chan[i].std.duty.val; + } + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val&3; + } + if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-32768,32767); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); + if (i<2) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock<<1,CHIP_DIVIDER); + if (chan[i].freq<1) chan[i].freq=1; + if (chan[i].freq>2047) chan[i].freq=2047; + if (chan[i].freqChanged || chan[i].initWrite) { + rWrite(0x10|(i<<2),chan[i].freq&0xff); + rWrite(0x11|(i<<2),(chan[i].freq>>8)&0x7); + } + chan[i].initWrite=false; + } else if (i==3) { + int ntPos=chan[i].baseFreq; + if (NEW_ARP_STRAT) { + if (chan[i].fixedArp) { + ntPos=chan[i].baseNoteOverride; + } else { + ntPos+=chan[i].arpOff; + } + } + ntPos+=chan[i].pitch2; + chan[i].freq=15-(ntPos&15); + unsigned char r=(chan[i].freq<<4)|(chan[i].outVol&0xf); + rWrite(0x28,r); + noiseReg[0]=r; + rWrite(0x29,0xc8); + r=((chan[i].duty&1)^dutySwap)|(0x02|0x10)|(chan[i].pan<<2); + rWrite(0x2A,r); + } + if (chan[i].keyOn && i==2) { + if (chan[i].pcm) { + int ntPos=chan[i].sampleNote; + ntPos+=chan[i].pitch2; + chan[i].freq=3-(ntPos&3); + int sNum=chan[i].sample; + DivSample* sample=parent->getSample(sNum); + if (sample!=NULL && sNum>=0 && sNumsong.sampleLen) { + unsigned int off=MIN(sampleOff[sNum]+chan[i].hasOffset,sampleOff[sNum]+sampleLen[sNum]); + unsigned int len=MAX((((int)sampleLen[sNum])-((int)chan[i].hasOffset)),0); + if (len) { + rWrite(0x18,off&0xff); + rWrite(0x19,(off>>8&0x3f)|0x80); + rWrite(0x1A,MIN(MAX(len>>4,0),255)); + rWrite(0x1B,chan[i].freq|((chan[i].pan&3)<<2)|((off>>14&7)<<4)); + rWrite(0x1C,0x80); + } + sampleOffset=chan[i].hasOffset; + chan[i].hasOffset=0; + } + } + } + if (chan[i].keyOff && i==2) { + if (chan[i].pcm) { + int ntPos=chan[i].sampleNote; + ntPos+=chan[i].pitch2; + chan[i].freq=3-(ntPos&3); + int sNum=chan[i].sample; + DivSample* sample=parent->getSample(sNum); + if (sample!=NULL && sNum>=0 && sNumsong.sampleLen) { + rWrite(0x1C,0x00); + } + } + } + if (chan[i].keyOn) chan[i].kon=true; + if (chan[i].keyOff) chan[i].kon=false; + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + + if (chan[i].kon) { + if (i<2) { + rWrite(0x12|(i<<2),(chan[i].outVol&0xf)|((chan[i].duty&3)<<4)); + rWrite(0x13|(i<<2),0xc8); + } else if (i == 3) { + int ntPos=chan[i].baseFreq; + if (NEW_ARP_STRAT) { + if (chan[i].fixedArp) { + ntPos=chan[i].baseNoteOverride; + } else { + ntPos+=chan[i].arpOff; + } + } + ntPos+=chan[i].pitch2; + chan[i].freq=15-(ntPos&15); + unsigned char r=(chan[i].freq<<4)|(chan[i].outVol&0xf); + if (noiseReg[0] != r) rWrite(0x28,r); + noiseReg[0]=r; + rWrite(0x29,0xc8); + r=((chan[i].duty&1)^dutySwap)|(0x02|0x10)|(chan[i].pan<<2); + if (noiseReg[2] != r) rWrite(0x2A,r); + noiseReg[2]=r; + } else if (i==2) { + if (chan[i].pcm) { + int ntPos=chan[i].sampleNote; + ntPos+=chan[i].pitch2; + chan[i].freq=3-(ntPos&3); + int sNum=chan[i].sample; + DivSample* sample=parent->getSample(sNum); + if (sample!=NULL && sNum>=0 && sNumsong.sampleLen) { + unsigned int off=MIN(sampleOff[sNum]+sampleOffset,sampleOff[sNum]+sampleLen[sNum]); + unsigned int len=MAX((((int)sampleLen[sNum])-((int)sampleOffset)),0); + if (len) { + rWrite(0x1A,MIN(MAX(len>>4,0),255)); + if (chan[i].outVol==0) { + rWrite(0x1B,chan[i].freq|((off>>14&7)<<4)); + } else { + rWrite(0x1B,chan[i].freq|((chan[i].pan&3)<<2)|((off>>14&7)<<4)); + } + } + } + } + } + } else { + if (i < 2) { + rWrite(0x12|(i<<2),0); + rWrite(0x13|(i<<2),0xc8); + } else if (i == 3) { + rWrite(0x29,0); + unsigned char r=0; + if (noiseReg[2] != r) rWrite(0x2A,r); + noiseReg[2]=r; + } + } + + } +} + +int DivPlatformSupervision::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SUPERVISION); + if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) { + chan[c.chan].pcm=true; + } else { + chan[c.chan].pcm=false; + } + if (chan[c.chan].pcm) { + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; + if (c.chan==2) { + c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNote=c.value; + } + } + } else { + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; + } + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=c.chan==3?c.value:NOTE_PERIODIC(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + //chwrite(c.chan,0x04,0x80|chan[c.chan].vol); + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + if (chan[c.chan].active) { + //chwrite(c.chan,0x04,0x80|chan[c.chan].outVol); + } + } + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_STD_NOISE_MODE: + chan[c.chan].duty=c.value; + break; + case DIV_CMD_SAMPLE_POS: + chan[c.chan].hasOffset=c.value; + chan[c.chan].keyOn=true; + break; + case DIV_CMD_PANNING: { + chan[c.chan].pan=0; + if (c.value&0xf0) chan[c.chan].pan|=2; + if (c.value2>>4) chan[c.chan].pan|=1; + if (chan[c.chan].pan==0) chan[c.chan].pan=3; + break; + } + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + case DIV_CMD_PRE_PORTA: + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SUPERVISION)); + } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 15; + break; + case DIV_CMD_MACRO_OFF: + chan[c.chan].std.mask(c.value,true); + break; + case DIV_CMD_MACRO_ON: + chan[c.chan].std.mask(c.value,false); + break; + case DIV_CMD_MACRO_RESTART: + chan[c.chan].std.restart(c.value); + break; + default: + break; + } + return 1; +} + +void DivPlatformSupervision::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; +} + +void DivPlatformSupervision::forceIns() { + for (int i=0; i<4; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + //chwrite(i,0x05,isMuted[i]?0:chan[i].pan); + } +} + +void* DivPlatformSupervision::getChanState(int ch) { + return &chan[ch]; +} + +DivMacroInt* DivPlatformSupervision::getChanMacroInt(int ch) { + return &chan[ch].std; +} + +DivDispatchOscBuffer* DivPlatformSupervision::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +unsigned char* DivPlatformSupervision::getRegisterPool() { + return regPool; +} + +int DivPlatformSupervision::getRegisterPoolSize() { + return 64; +} + +void DivPlatformSupervision::reset() { + writes.clear(); + memset(regPool,0,64); + for (int i=0; i<4; i++) { + chan[i]=DivPlatformSupervision::Channel(); + chan[i].std.setEngine(parent); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + supervision_sound_reset(&svision); + memset(tempL,0,32*sizeof(int)); + memset(tempR,0,32*sizeof(int)); + memset(noiseReg,0,3*sizeof(unsigned char)); + noiseReg[2]=0xff; + sampleOffset=0; +} + +int DivPlatformSupervision::getOutputCount() { + return 2; +} + +bool DivPlatformSupervision::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformSupervision::notifyInsDeletion(void* ins) { + for (int i=0; i<4; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformSupervision::setFlags(const DivConfig& flags) { + if (flags.getInt("swapDuty",true)) { + dutySwap=1; + } else { + dutySwap=0; + } + otherFlags=0; + if (flags.getInt("sqStereo",false)) { + otherFlags |= 1; + } + chipClock=4000000; + CHECK_CUSTOM_CLOCK; + rate=chipClock/64; + for (int i=0; i<4; i++) { + oscBuf[i]->rate=rate; + } + supervision_sound_set_clock(&svision,(unsigned int)chipClock); + supervision_sound_set_flags(&svision,(unsigned int)otherFlags); +} + +void DivPlatformSupervision::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformSupervision::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +const void* DivPlatformSupervision::getSampleMem(int index) { + return index==0?sampleMem:NULL; +} + +size_t DivPlatformSupervision::getSampleMemCapacity(int index) { + return index==0?65536:0; +} + +size_t DivPlatformSupervision::getSampleMemUsage(int index) { + return index==0?sampleMemLen:0; +} + +bool DivPlatformSupervision::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +const DivMemoryComposition* DivPlatformSupervision::getMemCompo(int index) { + if (index!=0) return NULL; + return &memCompo; +} + +void DivPlatformSupervision::renderSamples(int sysID) { + memset(sampleMem,0,getSampleMemCapacity(0)); + memset(sampleLoaded,0,256*sizeof(bool)); + + memCompo=DivMemoryComposition(); + memCompo.name="Sample Memory"; + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOff[i]=0; + continue; + } + unsigned int paddedLen=((s->length8>>1)+31)&(~0x1f); + logV("%d padded length: %d",i,paddedLen); + if (paddedLen>=4096) { + paddedLen=4096; + } + if ((memPos&(~0x3fff))!=((memPos+paddedLen)&(~0x3fff))) { + memPos=(memPos+0x3fff)&(~0x3fff); + } + if (memPos>=getSampleMemCapacity(0)) { + logW("out of memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + sampleLen[i]=(getSampleMemCapacity(0)-memPos)>>1; + for (size_t i=0; i<(getSampleMemCapacity(0)-memPos)>>1; i++) { + sampleMem[memPos+i]=(((s->data8[i*2+0]+128)>>4)<<4&0xf0)|(((s->data8[i*2+1]+128)>>4)&0xf); + } + logW("out of memory for sample %d!",i); + } else { + size_t len=MIN((s->length8>>1),paddedLen); + sampleLen[i]=(unsigned int)(len); + for (size_t i=0; idata8[i*2+0]+128)>>4)<<4&0xf0)|(((s->data8[i*2+1]+128)>>4)&0xf); + } + sampleLoaded[i]=true; + } + sampleOff[i]=memPos; + memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_SAMPLE,"Sample",i,memPos,memPos+paddedLen)); + memPos+=paddedLen; + } + sampleMemLen=memPos; + + memCompo.capacity=65536; + memCompo.used=sampleMemLen; +} + +bool DivPlatformSupervision::getDCOffRequired() { + return true; +} + +int DivPlatformSupervision::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<4; i++) { + isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + } + sampleMem=svision.supervision_dma_mem; + dutySwap=0; + otherFlags=0; + sampleOffset=0; + sampleMemLen=0; + memset(sampleMem,0,65536); + svision.ch_mask=15; + svision.flags=1; + setFlags(flags); + reset(); + return 4; +} + +void DivPlatformSupervision::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } +} + +DivPlatformSupervision::~DivPlatformSupervision() { +} diff --git a/src/engine/platform/supervision.h b/src/engine/platform/supervision.h new file mode 100644 index 000000000..8fb9dd550 --- /dev/null +++ b/src/engine/platform/supervision.h @@ -0,0 +1,104 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _SUPERVISION_H +#define _SUPERVISION_H + +#include "../dispatch.h" +#include "../../fixedQueue.h" +#include "sound/supervision.h" + +class DivPlatformSupervision: public DivDispatch { + struct Channel: public SharedChannel { + unsigned int duty, len, pan, pcm; // pcm is channel 3 ONLY + int sample, hasOffset; // again, for channel 3 ONLY + bool setPos, kon, initWrite; + Channel(): + SharedChannel(63), + duty(0), + len(0x1f), + pan(3), + pcm(false), + hasOffset(0), + setPos(false), + kon(false), + initWrite(true) {} + }; + Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; + bool isMuted[4]; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(): addr(0), val(9) {} + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + FixedQueue writes; + + int curChan; + int tempL[32]; + int tempR[32]; + int coreQuality; + unsigned char regPool[64]; + unsigned int sampleOff[256]; + unsigned int sampleLen[256]; + bool sampleLoaded[256]; + DivMemoryComposition memCompo; + unsigned char* sampleMem; + size_t sampleMemLen; + unsigned char dutySwap; + unsigned char otherFlags; + unsigned int sampleOffset; + unsigned char noiseReg[3]; + struct svision_t svision; + + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short** buf, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); + DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + int getOutputCount(); + bool keyOffAffectsArp(int ch); + void setFlags(const DivConfig& flags); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + const void* getSampleMem(int index); + size_t getSampleMemCapacity(int index); + size_t getSampleMemUsage(int index); + bool isSampleLoaded(int index, int sample); + const DivMemoryComposition* getMemCompo(int index); + void renderSamples(int chipID); + bool getDCOffRequired(); + int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); + void quit(); + ~DivPlatformSupervision(); +}; + +#endif diff --git a/src/engine/platform/upd1771c.cpp b/src/engine/platform/upd1771c.cpp new file mode 100644 index 000000000..d583771a7 --- /dev/null +++ b/src/engine/platform/upd1771c.cpp @@ -0,0 +1,366 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "upd1771c.h" +#include "../engine.h" +#include "../../ta-log.h" +#include "furIcons.h" +#include + +//#define rWrite(a,v) pendingWrites[a]=v; +#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} } + +#define CHIP_DIVIDER 64 + +const char* regCheatSheetUPD1771c[]={ + NULL +}; + +const char** DivPlatformUPD1771c::getRegisterSheet() { + return regCheatSheetUPD1771c; +} + +void DivPlatformUPD1771c::acquire(short** buf, size_t len) { + for (size_t h=0; hdata[oscBuf[0]->needle++]=s; + buf[0][h]=s; + buf[1][h]=s; + } +} + +void DivPlatformUPD1771c::tick(bool sysTick) { + for (int i=0; i<1; i++) { + + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31); + } + if (NEW_ARP_STRAT) { + chan[i].handleArp(); + } else if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + int f=parent->calcArp(chan[i].note,chan[i].std.arp.val); + chan[i].baseFreq=NOTE_PERIODIC(f); + } + chan[i].freqChanged=true; + } + if (chan[i].std.wave.had) { + chan[i].wave=chan[i].std.wave.val&7; + } + if (chan[i].std.duty.had) { + chan[i].duty=chan[i].std.duty.val&1; + } + if (chan[i].std.ex1.had) { + chan[i].pos=chan[i].std.ex1.val&31; + } + if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-32768,32767); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); + if (i==0) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); + } + if (chan[i].freqChanged || initWrite[i] || chan[i].keyOn) { + if (chan[i].duty == 0) { + rWrite(0,2); + rWrite(1,(chan[i].wave<<5)|chan[i].pos); + float p = ((float)chan[i].freq)/((float)(31-chan[i].pos))*31.0; + rWrite(2,MIN(MAX((int)p,0),255)); + rWrite(3,chan[i].outVol); + } else if (chan[i].duty == 1) { + rWrite(0,1); + rWrite(1,(chan[i].wave<<5)); + rWrite(2,MIN(MAX(chan[i].freq>>7,0),255)); + rWrite(3,chan[i].outVol); + } else { + rWrite(0,0); + } + initWrite[i]=0; + } + if (chan[i].keyOff) { + rWrite(0,0); + } + if (chan[i].keyOn) kon[i]=1; + if (chan[i].keyOff) kon[i]=0; + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + + if (kon[i]) { + if (i==0) { + if (chan[i].duty == 0) { + rWrite(0,2); + rWrite(1,(chan[i].wave<<5)|chan[i].pos); + // TODO: improve + float p = ((float)chan[i].freq)/((float)(32-chan[i].pos))*32.0; + rWrite(2,MIN(MAX((int)p,0),255)); + rWrite(3,chan[i].outVol); + } else if (chan[i].duty == 1) { + rWrite(0,1); + rWrite(1,(chan[i].wave<<5)); + rWrite(2,MIN(MAX(chan[i].freq>>7,0),255)); + rWrite(3,chan[i].outVol); + } else { + rWrite(0,0); + } + } + } else { + if (i == 0) { + rWrite(0,0); + } + } + + } +} + +int DivPlatformUPD1771c::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_UPD1771C); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=c.chan==3?c.value:NOTE_PERIODIC(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + //chwrite(c.chan,0x04,0x80|chan[c.chan].vol); + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + if (chan[c.chan].active) { + //chwrite(c.chan,0x04,0x80|chan[c.chan].outVol); + } + } + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_PERIODIC(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_STD_NOISE_MODE: + chan[c.chan].wave=c.value&7; + chan[c.chan].duty=c.value>>4&1; + break; + case DIV_CMD_N163_WAVE_POSITION: + chan[c.chan].pos=c.value; + break; + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + case DIV_CMD_PRE_PORTA: + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_UPD1771C)); + } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 31; + break; + case DIV_CMD_MACRO_OFF: + chan[c.chan].std.mask(c.value,true); + break; + case DIV_CMD_MACRO_ON: + chan[c.chan].std.mask(c.value,false); + break; + case DIV_CMD_MACRO_RESTART: + chan[c.chan].std.restart(c.value); + break; + default: + break; + } + return 1; +} + +void DivPlatformUPD1771c::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; +} + +void DivPlatformUPD1771c::forceIns() { + for (int i=0; i<1; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + //chwrite(i,0x05,isMuted[i]?0:chan[i].pan); + } +} + +void* DivPlatformUPD1771c::getChanState(int ch) { + return &chan[ch]; +} + +DivMacroInt* DivPlatformUPD1771c::getChanMacroInt(int ch) { + return &chan[ch].std; +} + +DivDispatchOscBuffer* DivPlatformUPD1771c::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +unsigned char* DivPlatformUPD1771c::getRegisterPool() { + return regPool; +} + +int DivPlatformUPD1771c::getRegisterPoolSize() { + return 16; +} + +void DivPlatformUPD1771c::reset() { + writes.clear(); + memset(regPool,0,16); + for (int i=0; i<1; i++) { + chan[i]=DivPlatformUPD1771c::Channel(); + chan[i].std.setEngine(parent); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + upd1771c_reset(&scv); + memset(tempL,0,32*sizeof(int)); + memset(tempR,0,32*sizeof(int)); + memset(kon,0,1*sizeof(unsigned char)); + memset(initWrite,1,1*sizeof(unsigned char)); +} + +int DivPlatformUPD1771c::getOutputCount() { + return 2; +} + +bool DivPlatformUPD1771c::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformUPD1771c::notifyInsDeletion(void* ins) { + for (int i=0; i<1; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformUPD1771c::setFlags(const DivConfig& flags) { + chipClock=6000000; + CHECK_CUSTOM_CLOCK; + rate=chipClock/32; + for (int i=0; i<1; i++) { + oscBuf[i]->rate=rate; + } + upd1771c_sound_set_clock(&scv,(unsigned int)chipClock,8); +} + +void DivPlatformUPD1771c::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformUPD1771c::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformUPD1771c::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<1; i++) { + isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + } + setFlags(flags); + reset(); + return 1; +} + +void DivPlatformUPD1771c::quit() { + for (int i=0; i<1; i++) { + delete oscBuf[i]; + } +} + +DivPlatformUPD1771c::~DivPlatformUPD1771c() { +} diff --git a/src/engine/platform/upd1771c.h b/src/engine/platform/upd1771c.h new file mode 100644 index 000000000..b225d342c --- /dev/null +++ b/src/engine/platform/upd1771c.h @@ -0,0 +1,84 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _UPD1771C_H +#define _UPD1771C_H + +#include "../dispatch.h" +#include "../../fixedQueue.h" +#include "sound/upd1771c.h" + +class DivPlatformUPD1771c: public DivDispatch { + struct Channel: public SharedChannel { + unsigned int wave; + int pos, duty; + Channel(): + SharedChannel(15), + wave(0), + pos(0), + duty(0) {} + }; + Channel chan[1]; + DivDispatchOscBuffer* oscBuf[1]; + bool isMuted[4]; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(): addr(0), val(9) {} + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + FixedQueue writes; + + int curChan; + int tempL[32]; + int tempR[32]; + int coreQuality; + unsigned char regPool[16]; + unsigned char kon[1]; + unsigned char initWrite[1]; + struct upd1771c_t scv; + + + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short** buf, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); + DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + int getOutputCount(); + bool keyOffAffectsArp(int ch); + void setFlags(const DivConfig& flags); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); + void quit(); + ~DivPlatformUPD1771c(); +}; + +#endif diff --git a/src/engine/song.h b/src/engine/song.h index 0202a7ed7..32f6fb5ef 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -141,6 +141,8 @@ enum DivSystem { DIV_SYSTEM_5E01, DIV_SYSTEM_BIFURCATOR, DIV_SYSTEM_SID2, + DIV_SYSTEM_SUPERVISION, + DIV_SYSTEM_UPD1771C, DIV_SYSTEM_MAX }; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index e0b755973..3b36f03ff 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1941,6 +1941,33 @@ void DivEngine::registerSystems() { {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); + sysDefs[DIV_SYSTEM_SUPERVISION]=new DivSysDef( + _("Watara Supervision"), NULL, 0xe3, 0, 4, false, true, 0, false, 0, 0, 0, + _("a handheld that was trying to compete with the Game Boy, but it never succeded."), + {_("Pulse 1"), _("Pulse 2"), _("PCM"), _("Noise")}, + {"S1", "S2", "PCM", "NO"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_NOISE}, + {DIV_INS_SUPERVISION, DIV_INS_SUPERVISION, DIV_INS_SUPERVISION, DIV_INS_SUPERVISION}, + {DIV_INS_SUPERVISION, DIV_INS_SUPERVISION, DIV_INS_AMIGA, DIV_INS_SUPERVISION}, + { + {0x12, {DIV_CMD_STD_NOISE_MODE, _("12xx: Set duty cycle/noise mode (pulse: 0 to 3; noise: 0 or 1)")}}, + } + ); + + sysDefs[DIV_SYSTEM_UPD1771C]=new DivSysDef( + _("NEC μPD1771C"), NULL, 0xe4, 0, 1, false, true, 0, false, 0, 0, 0, + _("this was an SoC with some funky wavetable/noise hardware"), + {_("Wave/Noise")}, + {"W"}, + {DIV_CH_NOISE}, + {DIV_INS_UPD1771C}, + {}, + { + {0x10, {DIV_CMD_STD_NOISE_MODE, _("10xx: Set duty/waveform (bit 0-3: waveform; bit 4: mode)")}}, + {0x12, {DIV_CMD_N163_WAVE_POSITION, _("12xx: Set waveform position (0-31)")}}, + } + ); + sysDefs[DIV_SYSTEM_SM8521]=new DivSysDef( _("Sharp SM8521"), NULL, 0xc8, 0, 3, false, true, 0, false, 0, 32, 16, _("a SoC with wavetable sound hardware."), diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index 3cb6b4c13..141ce50b2 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -51,6 +51,7 @@ #include "../engine/platform/pcmdac.h" #include "../engine/platform/k007232.h" #include "../engine/platform/ga20.h" +#include "../engine/platform/supervision.h" #include "../engine/platform/sm8521.h" #include "../engine/platform/pv1000.h" #include "../engine/platform/k053260.h" diff --git a/src/gui/gui.h b/src/gui/gui.h index dc097734b..c48d3615e 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -354,6 +354,8 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_GBA_MINMOD, GUI_COLOR_INSTR_BIFURCATOR, GUI_COLOR_INSTR_SID2, + GUI_COLOR_INSTR_SUPERVISION, + GUI_COLOR_INSTR_UPD1771C, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index c1e42508e..c09e79edc 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -184,6 +184,8 @@ const char* insTypes[DIV_INS_MAX+1][3]={ {"GBA MinMod",ICON_FA_VOLUME_UP,ICON_FUR_INS_GBA_MINMOD}, {"Bifurcator",ICON_FA_LINE_CHART,ICON_FUR_INS_BIFURCATOR}, {"SID2",ICON_FA_KEYBOARD_O,ICON_FUR_INS_SID2}, + {"Watara Supervision",ICON_FA_GAMEPAD,ICON_FUR_INS_SUPERVISION}, + {"NEC μPD1771C",ICON_FA_BAR_CHART,ICON_FUR_INS_UPD1771C}, {NULL,ICON_FA_QUESTION,ICON_FA_QUESTION} }; @@ -1018,6 +1020,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_GBA_MINMOD,"",ImVec4(0.5f,0.45f,0.7f,1.0f)), D(GUI_COLOR_INSTR_BIFURCATOR,"",ImVec4(0.8925f,0.8925f,0.8925f,1.0f)), D(GUI_COLOR_INSTR_SID2,"",ImVec4(0.6f,0.75f,1.0f,1.0f)), + D(GUI_COLOR_INSTR_SUPERVISION,"",ImVec4(0.52f,1.0f,0.6f,1.0f)), + D(GUI_COLOR_INSTR_UPD1771C,"",ImVec4(0.94f,0.52f,0.6f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)), @@ -1268,6 +1272,8 @@ const int availableSystems[]={ DIV_SYSTEM_SID2, DIV_SYSTEM_OPL4, DIV_SYSTEM_OPL4_DRUMS, + DIV_SYSTEM_SUPERVISION, + DIV_SYSTEM_UPD1771C, 0 // don't remove this last one! }; @@ -1365,6 +1371,8 @@ const int chipsSpecial[]={ DIV_SYSTEM_5E01, DIV_SYSTEM_BIFURCATOR, DIV_SYSTEM_SID2, + DIV_SYSTEM_SUPERVISION, + DIV_SYSTEM_UPD1771C, 0 // don't remove this last one! }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 72c9e6575..fd30812df 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2667,7 +2667,8 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) { ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_VRC6 || ins->type==DIV_INS_SU || - ins->type==DIV_INS_NDS) { + ins->type==DIV_INS_NDS || + ins->type==DIV_INS_SUPERVISION) { P(ImGui::Checkbox(_("Use sample"),&ins->amiga.useSample)); if (ins->type==DIV_INS_X1_010) { if (ImGui::InputInt(_("Sample bank slot##BANKSLOT"),&ins->x1_010.bankSlot,1,4)) { PARAMETER @@ -6253,7 +6254,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_C219 || ins->type==DIV_INS_NDS || ins->type==DIV_INS_GBA_DMA || - ins->type==DIV_INS_GBA_MINMOD) { + ins->type==DIV_INS_GBA_MINMOD || + ins->type==DIV_INS_SUPERVISION) { insTabSample(ins); } if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem("Namco 163")) { @@ -7427,6 +7429,13 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc(_("Pulse Width"),&ins->std.dutyMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); break; + case DIV_INS_SUPERVISION: + macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,15,160,uiColors[GUI_COLOR_MACRO_VOLUME])); + macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val)); + macroList.push_back(FurnaceGUIMacroDesc(_("Duty/Noise"),&ins->std.dutyMacro,0,3,160,uiColors[GUI_COLOR_MACRO_NOISE])); + macroList.push_back(FurnaceGUIMacroDesc(_("Noise/PCM Pan"),&ins->std.panLMacro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,panBits)); + macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); + break; case DIV_INS_SM8521: macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,31,160,uiColors[GUI_COLOR_MACRO_VOLUME])); macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val)); @@ -7569,7 +7578,14 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc(_("Noise Mode"),&ins->std.fmsMacro,0,3,64,uiColors[GUI_COLOR_MACRO_NOISE])); macroList.push_back(FurnaceGUIMacroDesc(_("Wave Mix"),&ins->std.amsMacro,0,3,64,uiColors[GUI_COLOR_MACRO_OTHER])); break; - + case DIV_INS_UPD1771C: + macroList.push_back(FurnaceGUIMacroDesc(_("Volume"),&ins->std.volMacro,0,31,160,uiColors[GUI_COLOR_MACRO_VOLUME])); + macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val)); + macroList.push_back(FurnaceGUIMacroDesc(_("Waveform"),&ins->std.waveMacro,0,7,160,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,false,NULL)); + macroList.push_back(FurnaceGUIMacroDesc(_("Wave Pos"),&ins->std.ex1Macro,0,31,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(_("Duty/Mode"),&ins->std.dutyMacro,0,1,160,uiColors[GUI_COLOR_MACRO_NOISE])); + macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); + break; case DIV_INS_MAX: case DIV_INS_NULL: break; diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index d32ed9829..ae2ae7672 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -320,6 +320,11 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_NDS, 1.0f, 0, "") } ); + ENTRY( + "Watara Supervision", { + CH(DIV_SYSTEM_SUPERVISION, 1.0f, 0, "") + } + ); CATEGORY_END; CATEGORY_BEGIN("Computers","let's get to work on chiptune today."); @@ -1331,6 +1336,11 @@ void FurnaceGUI::initSystemPresets() { ) } ); + ENTRY( + "Epoch Super Cassette Vision", { + CH(DIV_SYSTEM_UPD1771C, 1.0f, 0, "") + } + ); CATEGORY_END; CATEGORY_BEGIN("Arcade systems","INSERT COIN"); @@ -3176,6 +3186,16 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_SID2, 1.0f, 0, "") } ); + ENTRY( + "Watara Supervision", { + CH(DIV_SYSTEM_SUPERVISION, 1.0f, 0, "") + } + ); + ENTRY( + "NEC μPD1771C", { + CH(DIV_SYSTEM_UPD1771C, 1.0f, 0, "") + } + ); CATEGORY_END; CATEGORY_BEGIN("DefleMask-compatible","these configurations are compatible with DefleMask.\nselect this if you need to save as .dmf or work with that program."); diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 2df3aab0c..9a6000820 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -419,6 +419,19 @@ void FurnaceGUI::drawSampleEdit() { SAMPLE_WARN(warnLength,_("OPL4: maximum sample length is 65535")); } break; + case DIV_SYSTEM_SUPERVISION: + if (sample->loop) { + if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) { + SAMPLE_WARN(warnLoopPos,_("Supervision: loop point ignored on sample channel")); + } + } + if (sample->samples&31) { + SAMPLE_WARN(warnLength,_("Supervision: sample length will be padded to multiple of 32")); + } + if (sample->samples>8192) { + SAMPLE_WARN(warnLength,_("Supervision: maximum sample length is 8192")); + } + break; default: break; } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 298fa5ea3..1b168b72d 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -4144,6 +4144,8 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_INSTR_GBA_MINMOD,_("GBA MinMod")); UI_COLOR_CONFIG(GUI_COLOR_INSTR_BIFURCATOR,_("Bifurcator")); UI_COLOR_CONFIG(GUI_COLOR_INSTR_SID2,_("SID2")); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SUPERVISION,_("Supervision")); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_UPD1771C,_("μPD1771C")); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,_("Other/Unknown")); ImGui::TreePop(); } diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index e55df4d3e..997c3674e 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -2330,6 +2330,29 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl } break; } + case DIV_SYSTEM_SUPERVISION: { + bool swapDuty=flags.getInt("swapDuty",true); + + if (ImGui::Checkbox(_("Swap noise duty cycles"),&swapDuty)) { + altered=true; + } + + bool sqStereo=flags.getInt("sqStereo",false); + + if (ImGui::Checkbox(_("Stereo pulse waves"),&sqStereo)) { + altered=true; + } + + if (altered) { + e->lockSave([&]() { + flags.set("swapDuty",(int)swapDuty); + }); + e->lockSave([&]() { + flags.set("sqStereo",(int)sqStereo); + }); + } + break; + } case DIV_SYSTEM_SM8521:/* { bool noAntiClick=flags.getBool("noAntiClick",false); @@ -2589,6 +2612,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl case DIV_SYSTEM_C219: case DIV_SYSTEM_BIFURCATOR: case DIV_SYSTEM_POWERNOISE: + case DIV_SYSTEM_UPD1771C: break; case DIV_SYSTEM_YMU759: case DIV_SYSTEM_ESFM: diff --git a/src/gui/sysPartNumber.cpp b/src/gui/sysPartNumber.cpp index 1ce6e2f38..5211ce814 100644 --- a/src/gui/sysPartNumber.cpp +++ b/src/gui/sysPartNumber.cpp @@ -279,6 +279,12 @@ const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) { break; case DIV_SYSTEM_ESFM: return "ES1xxx"; + case DIV_SYSTEM_SUPERVISION: + return "Watara Supervision"; + break; + case DIV_SYSTEM_UPD1771C: + return "μPD1771C"; + break; default: return FurnaceGUI::getSystemName(sys); break; diff --git a/src/icon/furIcons.h b/src/icon/furIcons.h index 9712f5582..71df76fd2 100644 --- a/src/icon/furIcons.h +++ b/src/icon/furIcons.h @@ -75,6 +75,8 @@ #define ICON_FUR_INS_GBA_MINMOD u8"\ue15f" #define ICON_FUR_INS_BIFURCATOR u8"\ue160" #define ICON_FUR_INS_SID2 u8"\ue161" +#define ICON_FUR_INS_SUPERVISION u8"\ue162" +#define ICON_FUR_INS_UPD1771C u8"\ue163" // sample editor #define ICON_FUR_SAMPLE_APPLY_SILENCE u8"\ue136"