mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-22 12:35:11 +00:00
Add VIC-20 support
This commit is contained in:
parent
6c10c269a1
commit
feb138cefc
9 changed files with 749 additions and 3 deletions
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
253
src/engine/platform/sound/vic20sound.c
Normal file
253
src/engine/platform/sound/vic20sound.c
Normal 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;
|
||||
}
|
69
src/engine/platform/sound/vic20sound.h
Normal file
69
src/engine/platform/sound/vic20sound.h
Normal 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
|
329
src/engine/platform/vic20.cpp
Normal file
329
src/engine/platform/vic20.cpp
Normal 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() {
|
||||
}
|
86
src/engine/platform/vic20.h
Normal file
86
src/engine/platform/vic20.h
Normal 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
|
|
@ -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));
|
||||
|
|
|
@ -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!
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue