From feb138cefc5d08f21f5768fbc582c364e166c5cd Mon Sep 17 00:00:00 2001 From: Natt Akuma Date: Tue, 22 Mar 2022 14:48:48 +0700 Subject: [PATCH] Add VIC-20 support --- CMakeLists.txt | 3 + src/engine/dispatchContainer.cpp | 4 + src/engine/platform/sound/vic20sound.c | 253 +++++++++++++++++++ src/engine/platform/sound/vic20sound.h | 69 ++++++ src/engine/platform/vic20.cpp | 329 +++++++++++++++++++++++++ src/engine/platform/vic20.h | 86 +++++++ src/engine/playback.cpp | 1 + src/gui/guiConst.cpp | 1 + src/gui/insEdit.cpp | 6 +- 9 files changed, 749 insertions(+), 3 deletions(-) create mode 100644 src/engine/platform/sound/vic20sound.c create mode 100644 src/engine/platform/sound/vic20sound.h create mode 100644 src/engine/platform/vic20.cpp create mode 100644 src/engine/platform/vic20.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f09c88a6..b95900c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -273,6 +273,8 @@ src/engine/platform/sound/swan.cpp src/engine/platform/sound/k005289/k005289.cpp +src/engine/platform/sound/vic20sound.c + src/engine/platform/ym2610Interface.cpp src/engine/blip_buf.c @@ -321,6 +323,7 @@ src/engine/platform/swan.cpp src/engine/platform/vera.cpp src/engine/platform/bubsyswsg.cpp src/engine/platform/pet.cpp +src/engine/platform/vic20.cpp src/engine/platform/dummy.cpp ) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index be429c35..0f553772 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -47,6 +47,7 @@ #include "platform/lynx.h" #include "platform/bubsyswsg.h" #include "platform/pet.h" +#include "platform/vic20.h" #include "platform/dummy.h" #include "../ta-log.h" #include "song.h" @@ -279,6 +280,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_PET: dispatch=new DivPlatformPET; break; + case DIV_SYSTEM_VIC20: + dispatch=new DivPlatformVIC20; + break; default: logW("this system is not supported yet! using dummy platform.\n"); dispatch=new DivPlatformDummy; diff --git a/src/engine/platform/sound/vic20sound.c b/src/engine/platform/sound/vic20sound.c new file mode 100644 index 00000000..8a42bc70 --- /dev/null +++ b/src/engine/platform/sound/vic20sound.c @@ -0,0 +1,253 @@ +/* + * vic20sound.c - Implementation of VIC20 sound code. + * + * Written by + * Rami Rasanen + * Ville-Matias Heikkila + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#include +#include +#include + +#include "vic20sound.h" + +/* ---------------------------------------------------------------------*/ + +static float voltagefunction[] = { + 0.00f, 148.28f, 296.55f, 735.97f, 914.88f, 1126.89f, 1321.86f, 1503.07f, 1603.50f, + 1758.00f, 1913.98f, 2070.94f, 2220.36f, 2342.91f, 2488.07f, 3188.98f, 3285.76f, 3382.53f, + 3479.31f, 3576.08f, 3672.86f, 3769.63f, 3866.41f, 3963.18f, 4059.96f, 4248.10f, 4436.24f, + 4624.38f, 4812.53f, 5000.67f, 5188.81f, 5192.91f, 5197.00f, 5338.52f, 5480.04f, 5621.56f, + 5763.07f, 5904.59f, 6046.11f, 6187.62f, 6329.14f, 6609.31f, 6889.47f, 7169.64f, 7449.80f, + 7729.97f, 7809.36f, 7888.75f, 7968.13f, 8047.52f, 8126.91f, 8206.30f, 8285.69f, 8365.07f, + 8444.46f, 8523.85f, 8603.24f, 8905.93f, 9208.63f, 9511.32f, 9814.02f, 9832.86f, 9851.70f, + 9870.54f, 9889.38f, 9908.22f, 9927.07f, 9945.91f, 9964.75f, 9983.59f, 10002.43f, 10021.27f, + 10040.12f, 10787.23f, 11534.34f, 12281.45f, 12284.98f, 12288.50f, 12292.03f, 12295.56f, 12299.09f, + 12302.62f, 12306.15f, 12309.68f, 12313.21f, 12316.74f, 12320.26f, 12323.79f, 12327.32f, 13113.05f, + 13898.78f, 13910.58f, 13922.39f, 13934.19f, 13945.99f, 13957.80f, 13969.60f, 13981.40f, 13993.21f, + 14005.01f, 14016.81f, 14028.62f, 14040.42f, 14052.22f, 14064.03f, 16926.31f, 16987.04f, 17047.77f, + 17108.50f, 17169.23f, 17229.96f, 17290.69f, 17351.42f, 17412.15f, 17472.88f, 17533.61f, 17594.34f, + 17655.07f, 17715.80f, 17776.53f, 17837.26f, 18041.51f, 18245.77f, 18450.02f, 18654.28f, 18858.53f, + 19062.78f, 19267.04f, 19471.29f, 19675.55f, 19879.80f, 20084.05f, 20288.31f, 20417.74f, 20547.17f, + 20676.61f, 20774.26f, 20871.91f, 20969.55f, 21067.20f, 21164.85f, 21262.50f, 21360.15f, 21457.80f, + 21555.45f, 21653.09f, 21750.74f, 21848.39f, 21946.04f, 22043.69f, 22141.34f, 22212.33f, 22283.33f, + 22354.33f, 22425.33f, 22496.32f, 22567.32f, 22638.32f, 22709.32f, 22780.31f, 22851.31f, 22922.31f, + 22993.31f, 23064.30f, 23135.30f, 23206.30f, 23255.45f, 23304.60f, 23353.75f, 23402.91f, 23452.06f, + 23501.21f, 23550.36f, 23599.51f, 23648.67f, 23768.81f, 23888.96f, 24009.11f, 24129.26f, 24249.41f, + 24369.56f, 24451.92f, 24534.28f, 24616.63f, 24698.99f, 24781.35f, 24863.70f, 24946.06f, 25028.42f, + 25110.77f, 25193.13f, 25275.49f, 25357.84f, 25440.20f, 25522.56f, 25604.92f, 25658.87f, 25712.83f, + 25766.79f, 25820.75f, 25874.71f, 25928.66f, 25982.62f, 26036.58f, 26090.54f, 26144.49f, 26198.45f, + 26252.41f, 26306.37f, 26360.33f, 26414.28f, 26501.23f, 26588.17f, 26675.12f, 26762.06f, 26849.01f, + 26935.95f, 27022.90f, 27109.84f, 27196.78f, 27283.73f, 27370.67f, 27457.62f, 27544.56f, 27631.51f, + 27718.45f, 27726.89f, 27735.33f, 27743.78f, 27752.22f, 27760.66f, 27769.10f, 27777.54f, 27785.98f, + 27794.43f, 27802.87f, 27811.31f, 27819.75f, 27828.19f, 27836.63f, 27845.08f, 27853.52f, 27861.96f, + 27870.40f, 27878.84f, 27887.28f, 27895.73f, 27904.17f, 27912.61f, 27921.05f, 27929.49f, 27937.93f, + 27946.38f, 27954.82f, 27963.26f, 27971.70f, 27980.14f, 27988.58f, 27997.03f, 28005.47f, 28013.91f, + 28022.35f, 28030.79f, 28039.23f, 28047.68f, 28056.12f, 28064.56f, 28073.00f, 28081.44f, 28089.88f, + 28098.33f, 28106.77f, 28115.21f, 28123.65f, 28132.09f, 28140.53f, 28148.98f, 28157.42f, 28165.86f, + 28174.30f, 28182.74f, 28191.18f, 28199.63f, 28208.07f, 28216.51f, 28224.95f, 28233.39f, 28241.83f, + 28250.28f, 28258.72f, 28267.16f, 28275.60f, 28284.04f, 28292.48f, 28300.93f, 28309.37f, 28317.81f, + 28326.25f, 28334.69f, 28343.13f, 28351.58f, 28360.02f, 28368.46f, 28376.90f, 28385.34f, 28393.78f, + 28402.23f, 28410.67f, 28419.11f, 28427.55f, 28435.99f, 28444.43f, 28452.88f, 28461.32f, 28469.76f, + 28478.20f, 28486.64f, 28495.08f, 28503.53f, 28511.97f, 28520.41f, 28528.85f, 28537.29f, 28545.73f, + 28554.18f, 28562.62f, 28571.06f, 28579.50f, 28587.94f, 28596.38f, 28604.83f, 28613.27f, 28621.71f, + 28630.15f, 28638.59f, 28647.03f, 28655.48f, 28663.92f, 28672.36f, 28680.80f, 28689.24f, 28697.68f, + 28706.13f, 28714.57f, 28723.01f, 28731.45f, 28739.89f, 28748.33f, 28756.78f, 28765.22f, 28773.66f, + 28782.10f, 28790.54f, 28798.98f, 28807.43f, 28815.87f, 28824.31f, 28832.75f, 28841.19f, 28849.63f, + 28858.08f, 28866.52f, 28874.96f, 28883.40f, 28891.84f, 28900.28f, 28908.73f, 28917.17f, 28925.61f, + 28934.05f, 28942.49f, 28950.93f, 28959.38f, 28967.82f, 28976.26f, 28984.70f, 28993.14f, 29001.58f, + 29010.03f, 29018.47f, 29026.91f, 29035.35f, 29043.79f, 29052.23f, 29060.68f, 29069.12f, 29077.56f, + 29086.00f, 29094.44f, 29102.88f, 29111.33f, 29119.77f, 29128.21f, 29136.65f, 29145.09f, 29153.53f, + 29161.98f, 29170.42f, 29178.86f, 29187.30f, 29195.74f, 29204.18f, 29212.63f, 29221.07f, 29229.51f, + 29237.95f, 29246.39f, 29254.83f, 29263.28f, 29271.72f, 29280.16f, 29288.60f, 29297.04f, 29305.48f, + 29313.93f, 29322.37f, 29330.81f, 29339.25f, 29347.69f, 29356.13f, 29364.58f, 29373.02f, 29381.46f, + 29389.90f, 29398.34f, 29406.78f, 29415.23f, 29423.67f, 29432.11f, 29440.55f, 29448.99f, 29457.43f, + 29465.88f, 29474.32f, 29482.76f, 29491.20f +}; + +static void vic_sound_clock(sound_vic20_t *snd, uint32_t cycles); + +int vic_sound_machine_calculate_samples(sound_vic20_t *snd, int16_t *pbuf, int nr, int soc, int scc, uint32_t delta_t) +{ + int s = 0; + int i; + float o; + int16_t vicbuf; + int samples_to_do; + + while (s < nr && delta_t >= snd->cycles_per_sample - snd->leftover_cycles) { + samples_to_do = (int)(snd->cycles_per_sample - snd->leftover_cycles); + snd->leftover_cycles += samples_to_do - snd->cycles_per_sample; + vic_sound_clock(snd, samples_to_do); + + o = snd->lowpassbuf - snd->highpassbuf; + snd->highpassbuf += snd->highpassbeta * (snd->lowpassbuf - snd->highpassbuf); + snd->lowpassbuf += snd->lowpassbeta * (voltagefunction[(((snd->accum * 7) / snd->accum_cycles) + 1) * snd->volume] - snd->lowpassbuf); + + if (o < -32768) { + vicbuf = -32768; + } else if (o > 32767) { + vicbuf = 32767; + } else { + vicbuf = (int16_t)o; + } + + for (i = 0; i < soc; i++) { + pbuf[(s * soc) + i] = vicbuf; + } + s++; + snd->accum = 0; + snd->accum_cycles = 0; + delta_t -= samples_to_do; + } + if (delta_t > 0) { + snd->leftover_cycles += delta_t; + vic_sound_clock(snd, delta_t); + delta_t = 0; + } + return s; +} + +static uint16_t noise_LFSR = 0x0000; +static uint8_t noise_LFSR0_old = 0; + +static void vic_sound_clock(sound_vic20_t *snd, uint32_t cycles) +{ + uint32_t i; + int j, enabled; + + if (cycles <= 0) { + return; + } + + for (j = 0; j < 4; j++) { + int chspeed = "\4\3\2\1"[j]; + + if (snd->ch[j].ctr > cycles) { + snd->accum += snd->ch[j].out * cycles; + snd->ch[j].ctr -= cycles; + } else { + for (i = cycles; i; i--) { + snd->ch[j].ctr--; + if (snd->ch[j].ctr <= 0) { + int a = (~snd->ch[j].reg) & 127; + int edge_trigger; + a = a ? a : 128; + snd->ch[j].ctr += a << chspeed; + enabled = (snd->ch[j].reg & 128) >> 7; + edge_trigger = (noise_LFSR & 1) & !noise_LFSR0_old; + + if((j != 3) || ((j == 3) && edge_trigger)) { + uint8_t shift = snd->ch[j].shift; + shift = ((shift << 1) | (((((shift & 128) >> 7)) ^ 1) & enabled)); + snd->ch[j].shift = shift; + } + if(j == 3) { + int bit3 = (noise_LFSR >> 3) & 1; + int bit12 = (noise_LFSR >> 12) & 1; + int bit14 = (noise_LFSR >> 14) & 1; + int bit15 = (noise_LFSR >> 15) & 1; + int gate1 = bit3 ^ bit12; + int gate2 = bit14 ^ bit15; + int gate3 = (gate1 ^ gate2) ^ 1; + int gate4 = (gate3 & enabled) ^ 1; + noise_LFSR0_old = noise_LFSR & 1; + noise_LFSR = (noise_LFSR << 1) | gate4; + } + snd->ch[j].out = snd->ch[j].shift & (j == 3 ? enabled : 1); + } + snd->accum += snd->ch[j].out; /* FIXME: doesn't take DC offset into account */ + } + } + } + + snd->accum_cycles += cycles; +} + +void vic_sound_machine_store(sound_vic20_t *snd, uint16_t addr, uint8_t value) +{ + switch (addr) { + case 0xA: + snd->ch[0].reg = value; + break; + case 0xB: + snd->ch[1].reg = value; + break; + case 0xC: + snd->ch[2].reg = value; + break; + case 0xD: + snd->ch[3].reg = value; + break; + case 0xE: + snd->volume = value & 0x0f; + break; + } +} + +int vic_sound_machine_init(sound_vic20_t *snd, int speed, int cycles_per_sec) +{ + uint32_t i; + float dt; + + memset(snd, 0, sizeof(snd)); + + snd->cycles_per_sample = (float)cycles_per_sec / speed; + snd->leftover_cycles = 0.0f; + + snd->lowpassbuf = 0.0f; + snd->highpassbuf = 0.0f; + + snd->speed = speed; + + dt = 1.f / speed; + + /* + Audio output stage + + 5V + -----+ + audio| 1k | + +---+---R---+--------(K) +----- + out | | | | |audio + -----+ C .01 C .1 | 1 uF | + | uF | uF +-----C-----+ 1K + | | + GND GND R 470 | amp + | +----- + + GND + + */ + + /* Low-pass: R = 1 kOhm, C = 100 nF; RC = 1e3*1e-7 = 1e-4 (1591 Hz) */ + snd->lowpassbeta = dt / ( dt + 1e-4f ); + /* High-pass: R = 1 kOhm, C = 1 uF; RC = 1e3*1e-6 = 1e-3 ( 159 Hz) */ + snd->highpassbeta = dt / ( dt + 1e-3f ); + + for (i = 0; i < 16; i++) { + vic_sound_machine_store(snd, (uint16_t)i, 0); + } + + return 1; +} diff --git a/src/engine/platform/sound/vic20sound.h b/src/engine/platform/sound/vic20sound.h new file mode 100644 index 00000000..33483330 --- /dev/null +++ b/src/engine/platform/sound/vic20sound.h @@ -0,0 +1,69 @@ +/* + * vic20sound.h - implementation of VIC20 sound code + * + * Written by + * Teemu Rantanen + * + * This file is part of VICE, the Versatile Commodore Emulator. + * See README for copyright notice. + * + * 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + * + */ + +#ifndef VICE_VIC20SOUND_H +#define VICE_VIC20SOUND_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct sound_vic20_s { + unsigned char div; + struct { + unsigned char out; + unsigned char reg; + unsigned char shift; + signed short ctr; + } ch[4]; + unsigned short noisectr; + unsigned char volume; + int cyclecount; + + int accum; + int accum_cycles; + + float cycles_per_sample; + float leftover_cycles; + int speed; + + float highpassbuf; + float highpassbeta; + float lowpassbuf; + float lowpassbeta; +}; +typedef struct sound_vic20_s sound_vic20_t; + +int vic_sound_machine_init(sound_vic20_t *snd, int speed, int cycles_per_sec); +void vic_sound_machine_store(sound_vic20_t *snd, uint16_t addr, uint8_t value); +int vic_sound_machine_calculate_samples(sound_vic20_t *snd, int16_t *pbuf, int nr, int soc, int scc, uint32_t delta_t); + +#ifdef __cplusplus +}; +#endif +#endif diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp new file mode 100644 index 00000000..558bc212 --- /dev/null +++ b/src/engine/platform/vic20.cpp @@ -0,0 +1,329 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 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 "vic20.h" +#include "../engine.h" +#include + +#define rWrite(a,v) {regPool[(a)]=(v)&0xff; vic_sound_machine_store(vic,a,(v)&0xff);} + +#define CHIP_DIVIDER 32 +#define SAMP_DIVIDER 4 + +const char* regCheatSheetVIC[]={ + "CH1_Pitch", "0A", + "CH2_Pitch", "0B", + "CH3_Pitch", "0C", + "Noise_Pitch", "0D", + "Volume", "0E", + NULL +}; + +const char** DivPlatformVIC20::getRegisterSheet() { + return regCheatSheetVIC; +} + +const char* DivPlatformVIC20::getEffectName(unsigned char effect) { + switch (effect) { + case 0x10: + return "10xx: Change waveform"; + break; + } + return NULL; +} + +void DivPlatformVIC20::acquire(short* bufL, short* bufR, size_t start, size_t len) { + const unsigned char loadFreq[3] = {0x7e, 0x7d, 0x7b}; + const unsigned char wavePatterns[16] = { + 0b0, 0b10, 0b100, 0b110, 0b1000, 0b1010, 0b1011, 0b1110, + 0b10010, 0b10100, 0b10110, 0b11000, 0b11010, 0b100100, 0b101010, 0b101100 + }; + for (size_t h=start; h=0) { + if (chan[i].waveWriteCycle>=16*7) { + // empty shift register first + rWrite(10+i,126); + } else if (chan[i].waveWriteCycle>=16) { + unsigned bit=8-(chan[i].waveWriteCycle/16); + rWrite(10+i,loadFreq[i]|((wavePatterns[chan[i].wave]<calcFreq(chan[i].baseFreq,chan[i].pitch,true); + if (i<3) chan[i].freq>>=(2-i); + else chan[i].freq>>=1; + if (chan[i].freq<1) chan[i].freq=1; + if (chan[i].freq>127) chan[i].freq=0; + if (isMuted[i]) chan[i].keyOn=false; + if (chan[i].keyOn) { + if (i<3) { + // 128*16 cycles for lowest channel to finish counting at lowest freq + // 2*16 cycles for lowest channel to empty first 2 bits + // 7*16 cycles to write 7 bits + chan[i].waveWriteCycle=137*16-1; + hasWaveWrite=true; + } else { + rWrite(10+i,255-chan[i].freq); + } + chan[i].keyOn=false; + } else if (chan[i].freqChanged && chan[i].active && !isMuted[i]) { + rWrite(10+i,255-chan[i].freq); + } + if (chan[i].keyOff) { + rWrite(10+i,0); + chan[i].keyOff=false; + } + chan[i].freqChanged=false; + } + } +} + +int DivPlatformVIC20::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=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; + chan[c.chan].std.init(ins); + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].std.init(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; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.hadVol) { + calcAndWriteOutVol(c.chan,15); + } + } + break; + case DIV_CMD_GET_VOLUME: + return chan[c.chan].vol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_WAVE: + chan[c.chan].wave=c.value&0x0f; + chan[c.chan].keyOn=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_LEGATO: + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(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].std.init(parent->getIns(chan[c.chan].ins)); + } + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 15; + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformVIC20::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + if (mute) { + chan[ch].keyOff=true; + } else if (chan[ch].active) { + chan[ch].keyOn=true; + } +} + +void DivPlatformVIC20::forceIns() { + for (int i=0; i<4; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + writeOutVol(i); + } +} + +void* DivPlatformVIC20::getChanState(int ch) { + return &chan[ch]; +} + +unsigned char* DivPlatformVIC20::getRegisterPool() { + return regPool; +} + +int DivPlatformVIC20::getRegisterPoolSize() { + return 16; +} + +void DivPlatformVIC20::reset() { + memset(regPool,0,16); + for (int i=0; i<4; i++) { + chan[i]=Channel(); + } + vic_sound_machine_init(vic,rate,chipClock); + hasWaveWrite=false; + rWrite(14,15); +} + +bool DivPlatformVIC20::isStereo() { + return false; +} + +void DivPlatformVIC20::notifyInsDeletion(void* ins) { + for (int i=0; i<4; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformVIC20::setFlags(unsigned int flags) { + if (flags&1) { + chipClock=COLOR_PAL/4.0; + } else { + chipClock=COLOR_NTSC*2.0/7.0; + } + rate=chipClock/4; +} + +void DivPlatformVIC20::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformVIC20::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformVIC20::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<4; i++) { + isMuted[i]=false; + } + setFlags(flags); + vic=new sound_vic20_t(); + reset(); + return 4; +} + +void DivPlatformVIC20::quit() { + delete vic; +} + +DivPlatformVIC20::~DivPlatformVIC20() { +} diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h new file mode 100644 index 00000000..1c4d384b --- /dev/null +++ b/src/engine/platform/vic20.h @@ -0,0 +1,86 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 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 _VIC20_H +#define _VIC20_H + +#include "../dispatch.h" +#include "../macroInt.h" +#include "sound/vic20sound.h" +#include + +class DivPlatformVIC20: public DivDispatch { + struct Channel { + int freq, baseFreq, pitch, note; + unsigned char ins, pan; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; + int vol, outVol, wave, waveWriteCycle; + DivMacroInt std; + Channel(): + freq(0), + baseFreq(0), + pitch(0), + note(0), + ins(-1), + pan(255), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + inPorta(false), + vol(15), + outVol(15), + wave(0), + waveWriteCycle(-1) {} + }; + Channel chan[4]; + bool isMuted[4]; + bool hasWaveWrite; + + unsigned char regPool[16]; + sound_vic20_t* vic; + void updateWave(int ch); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(); + void muteChannel(int ch, bool mute); + void setFlags(unsigned int flags); + void notifyInsDeletion(void* ins); + bool isStereo(); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + const char* getEffectName(unsigned char effect); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); + ~DivPlatformVIC20(); + private: + void calcAndWriteOutVol(int ch, int env); + void writeOutVol(int ch); +}; + +#endif diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index cf3e1309..404901a5 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -315,6 +315,7 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe break; case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_PET: + case DIV_SYSTEM_VIC20: switch (effect) { case 0x10: // select waveform dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 695ab163..a25e831e 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -164,5 +164,6 @@ const int availableSystems[]={ DIV_SYSTEM_VERA, DIV_SYSTEM_BUBSYS_WSG, DIV_SYSTEM_PET, + DIV_SYSTEM_VIC20, 0 // don't remove this last one! }; \ No newline at end of file diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index a1ba5f57..38642007 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1584,7 +1584,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_AY8930) { dutyMax=255; } - if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC || ins->type==DIV_INS_PET) { + if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC || ins->type==DIV_INS_PET || ins->type==DIV_INS_VIC) { dutyMax=0; } if (ins->type==DIV_INS_PCE) { @@ -1609,7 +1609,7 @@ void FurnaceGUI::drawInsEdit() { bitMode=true; } if (ins->type==DIV_INS_STD) waveMax=0; - if (ins->type==DIV_INS_TIA) waveMax=15; + if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_VIC) waveMax=15; if (ins->type==DIV_INS_C64) waveMax=4; if (ins->type==DIV_INS_SAA1099) waveMax=2; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPZ) waveMax=0; @@ -1946,4 +1946,4 @@ void FurnaceGUI::drawInsEdit() { } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_INS_EDIT; ImGui::End(); -} \ No newline at end of file +}