Add VIC-20 support

This commit is contained in:
Natt Akuma 2022-03-22 14:48:48 +07:00
parent 6c10c269a1
commit feb138cefc
9 changed files with 749 additions and 3 deletions

View file

@ -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
)

View file

@ -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;

View file

@ -0,0 +1,253 @@
/*
* vic20sound.c - Implementation of VIC20 sound code.
*
* Written by
* Rami Rasanen <raipsu@users.sf.net>
* Ville-Matias Heikkila <viznut@iki.fi>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View file

@ -0,0 +1,69 @@
/*
* vic20sound.h - implementation of VIC20 sound code
*
* Written by
* Teemu Rantanen <tvr@cs.hut.fi>
*
* 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 <stdint.h>
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

View file

@ -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 <math.h>
#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<start+len; h++) {
if (hasWaveWrite) {
hasWaveWrite=false;
for (int i=0; i<3; i++) {
if (chan[i].waveWriteCycle>=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]<<bit)&0x80));
} else {
rWrite(10+i,255-chan[i].freq);
}
chan[i].waveWriteCycle-=SAMP_DIVIDER;
hasWaveWrite=true;
}
}
}
short samp;
vic_sound_machine_calculate_samples(vic,&samp,1,1,0,SAMP_DIVIDER);
bufL[h]=samp;
bufR[h]=samp;
}
}
void DivPlatformVIC20::calcAndWriteOutVol(int ch, int env) {
chan[ch].outVol=MIN(chan[ch].vol*env/15,15);
writeOutVol(ch);
}
void DivPlatformVIC20::writeOutVol(int ch) {
if (!isMuted[ch]) {
rWrite(14,chan[ch].outVol);
}
}
void DivPlatformVIC20::tick() {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.hadVol) {
int env=chan[i].std.vol;
calcAndWriteOutVol(i,env);
}
if (chan[i].std.hadArp) {
if (!chan[i].inPorta) {
if (chan[i].std.arpMode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp);
}
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arpMode && chan[i].std.finishedArp) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.hadWave) {
if (chan[i].wave!=chan[i].std.wave) {
chan[i].wave=chan[i].std.wave&0x0f;
chan[i].keyOn=true;
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->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<DivRegWrite>& 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() {
}

View file

@ -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 <queue>
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<DivRegWrite>& 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

View file

@ -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));

View file

@ -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!
};

View file

@ -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();
}
}