Merge branch 'tildearrow:master' into master

This commit is contained in:
Eknous 2023-07-23 16:44:05 +04:00 committed by GitHub
commit 1e770d52b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 926 additions and 41 deletions

View file

@ -79,6 +79,7 @@
#include "platform/sm8521.h"
#include "platform/pv1000.h"
#include "platform/k053260.h"
#include "platform/ted.h"
#include "platform/pcmdac.h"
#include "platform/dummy.h"
#include "../ta-log.h"
@ -507,6 +508,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_K053260:
dispatch=new DivPlatformK053260;
break;
case DIV_SYSTEM_TED:
dispatch=new DivPlatformTED;
break;
case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC;
break;

View file

@ -961,6 +961,8 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
featureSM=true;
featureSL=true;
break;
case DIV_INS_TED:
break;
case DIV_INS_MAX:
break;

View file

@ -81,6 +81,8 @@ enum DivInstrumentType: unsigned short {
DIV_INS_SM8521=48,
DIV_INS_PV1000=49,
DIV_INS_K053260=50,
// DIV_INS_YMF292=51,
DIV_INS_TED=52,
DIV_INS_MAX,
DIV_INS_NULL
};

View file

@ -31,10 +31,6 @@ const char** DivPlatformMSM6258::getRegisterSheet() {
}
void DivPlatformMSM6258::acquire(short** buf, size_t len) {
short* outs[2]={
&msmOut,
NULL
};
for (size_t h=0; h<len; h++) {
if (--msmClockCount<0) {
if (--msmDividerCount<=0) {
@ -71,7 +67,7 @@ void DivPlatformMSM6258::acquire(short** buf, size_t len) {
}
}
msm->sound_stream_update(outs,1);
msm->sound_stream_update(&msmOut,1);
msmDividerCount=msmDivider;
}
msmClockCount=msmClock;

View file

@ -135,9 +135,9 @@ void okim6258_device::device_reset()
// sound_stream_update - handle a stream update
//-------------------------------------------------
void okim6258_device::sound_stream_update(short** outputs, int len)
void okim6258_device::sound_stream_update(short* output, int len)
{
short* buffer = outputs[0];
short* buffer = output;
if (m_status & STATUS_PLAYING)
{

View file

@ -43,7 +43,7 @@ public:
void device_clock_changed();
// sound stream updates
void sound_stream_update(short** outputs, int len);
void sound_stream_update(short* output, int len);
private:
int16_t clock_adpcm(uint8_t nibble);

View file

@ -0,0 +1,231 @@
/*
* ted-sound.c
*
* Written by
* Andreas Boose <viceteam@t-online.de>
* Tibor Biczo <crown @ axelero . hu>
* Marco van den Heuvel <blackystardust68@yahoo.com>
*
* 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 "ted-sound.h"
/* ------------------------------------------------------------------------- */
/* FIXME: Find proper volume multiplier. */
const int16_t volume_tab[16] = {
0x0000, 0x0800, 0x1000, 0x1800, 0x2000, 0x2800, 0x3000, 0x3800,
0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff
};
int ted_sound_machine_calculate_samples(struct plus4_sound_s* snd, int16_t *pbuf, int nr, int scc)
{
int i;
int j;
int16_t volume;
if (snd->digital) {
for (i = 0; i < nr; i++) {
pbuf[i] = (snd->volume * (snd->voice0_output_enabled + snd->voice1_output_enabled));
}
} else {
for (i = 0; i < nr; i++) {
snd->sample_position_remainder += snd->sample_length_remainder;
if (snd->sample_position_remainder >= snd->speed) {
snd->sample_position_remainder -= snd->speed;
snd->sample_position_integer++;
}
snd->sample_position_integer += snd->sample_length_integer;
if (snd->sample_position_integer >= 8) {
/* Advance state engine */
uint32_t ticks = snd->sample_position_integer >> 3;
if (snd->voice0_accu <= ticks) {
uint32_t delay = ticks - snd->voice0_accu;
snd->voice0_sign ^= 1;
snd->voice0_accu = 1023 - snd->voice0_reload;
if (snd->voice0_accu == 0) {
snd->voice0_accu = 1024;
}
if (delay >= snd->voice0_accu) {
snd->voice0_sign = ((delay / snd->voice0_accu)
& 1) ? snd->voice0_sign ^ 1
: snd->voice0_sign;
snd->voice0_accu = snd->voice0_accu - (delay % snd->voice0_accu);
} else {
snd->voice0_accu -= delay;
}
} else {
snd->voice0_accu -= ticks;
}
if (snd->voice1_accu <= ticks) {
uint32_t delay = ticks - snd->voice1_accu;
snd->voice1_sign ^= 1;
snd->noise_shift_register
= (snd->noise_shift_register << 1) +
( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^
((snd->noise_shift_register >> 5) & 1) ^
((snd->noise_shift_register >> 4) & 1) ^
((snd->noise_shift_register >> 1) & 1));
snd->voice1_accu = 1023 - snd->voice1_reload;
if (snd->voice1_accu == 0) {
snd->voice1_accu = 1024;
}
if (delay >= snd->voice1_accu) {
snd->voice1_sign = ((delay / snd->voice1_accu)
& 1) ? snd->voice1_sign ^ 1
: snd->voice1_sign;
for (j = 0; j < (int)(delay / snd->voice1_accu);
j++) {
snd->noise_shift_register
= (snd->noise_shift_register << 1) +
( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^
((snd->noise_shift_register >> 5) & 1) ^
((snd->noise_shift_register >> 4) & 1) ^
((snd->noise_shift_register >> 1) & 1));
}
snd->voice1_accu = snd->voice1_accu - (delay % snd->voice1_accu);
} else {
snd->voice1_accu -= delay;
}
} else {
snd->voice1_accu -= ticks;
}
}
snd->sample_position_integer = snd->sample_position_integer & 7;
volume = 0;
if (snd->voice0_output_enabled && snd->voice0_sign) {
volume += snd->volume;
}
if (snd->voice1_output_enabled && !snd->noise && snd->voice1_sign) {
volume += snd->volume;
}
if (snd->voice1_output_enabled && snd->noise && (!(snd->noise_shift_register & 1))) {
volume += snd->volume;
}
pbuf[i] = volume;
}
}
return nr;
}
int ted_sound_machine_init(struct plus4_sound_s* snd, int speed, int cycles_per_sec)
{
uint8_t val;
memset(snd,0,sizeof(struct plus4_sound_s));
snd->speed = speed;
snd->sample_length_integer = cycles_per_sec / speed;
snd->sample_length_remainder = cycles_per_sec % speed;
snd->sample_position_integer = 0;
snd->sample_position_remainder = 0;
snd->noise_shift_register = 0;
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
val = snd->plus4_sound_data[3];
snd->volume = volume_tab[val & 0x0f];
snd->voice0_output_enabled = (val & 0x10) ? 1 : 0;
snd->voice1_output_enabled = (val & 0x60) ? 1 : 0;
snd->noise = ((val & 0x60) == 0x40) ? 1 : 0;
snd->digital = val & 0x80;
if (snd->digital) {
snd->voice0_sign = 1;
snd->voice0_accu = 0;
snd->voice1_sign = 1;
snd->voice1_accu = 0;
snd->noise_shift_register = 0;
}
return 1;
}
void ted_sound_machine_store(struct plus4_sound_s* snd, uint16_t addr, uint8_t val)
{
switch (addr) {
case 0x0e:
snd->plus4_sound_data[0] = val;
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
break;
case 0x0f:
snd->plus4_sound_data[1] = val;
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
break;
case 0x10:
snd->plus4_sound_data[2] = val & 3;
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
break;
case 0x11:
snd->volume = volume_tab[val & 0x0f];
snd->voice0_output_enabled = (val & 0x10) ? 1 : 0;
snd->voice1_output_enabled = (val & 0x60) ? 1 : 0;
snd->noise = ((val & 0x60) == 0x40) ? 1 : 0;
snd->digital = val & 0x80;
if (snd->digital) {
snd->voice0_sign = 1;
snd->voice0_accu = 0;
snd->voice1_sign = 1;
snd->voice1_accu = 0;
snd->noise_shift_register = 0;
}
snd->plus4_sound_data[3] = val;
break;
case 0x12:
snd->plus4_sound_data[4] = val & 3;
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
break;
}
}
uint8_t ted_sound_machine_read(struct plus4_sound_s* snd, uint16_t addr)
{
switch (addr) {
case 0x0e:
return snd->plus4_sound_data[0];
case 0x0f:
return snd->plus4_sound_data[1];
case 0x10:
return snd->plus4_sound_data[2] | 0xc0;
case 0x11:
return snd->plus4_sound_data[3];
case 0x12:
return snd->plus4_sound_data[4];
}
return 0;
}
void ted_sound_reset(struct plus4_sound_s* snd)
{
uint16_t i;
snd->noise_shift_register = 0;
for (i = 0x0e; i <= 0x12; i++) {
ted_sound_machine_store(snd,i,0);
}
}

View file

@ -0,0 +1,81 @@
/*
* ted-sound.h
*
* Written by
* Andreas Boose <viceteam@t-online.de>
* Marco van den Heuvel <blackystardust68@yahoo.com>
*
* 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_TEDSOUND_H
#define VICE_TEDSOUND_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
struct plus4_sound_s {
/* Voice 0 collect number of cycles elapsed */
uint32_t voice0_accu;
/* Voice 0 toggle sign and reload accu if accu reached 0 */
uint32_t voice0_reload;
/* Voice 0 sign of the square wave */
int16_t voice0_sign;
uint8_t voice0_output_enabled;
/* Voice 1 collect number of cycles elapsed */
uint32_t voice1_accu;
/* Voice 1 toggle sign and reload accu if accu reached 0 */
uint32_t voice1_reload;
/* Voice 1 sign of the square wave */
int16_t voice1_sign;
uint8_t voice1_output_enabled;
/* Volume multiplier */
int16_t volume;
/* 8 cycles units per sample */
uint32_t speed;
uint32_t sample_position_integer;
uint32_t sample_position_remainder;
uint32_t sample_length_integer;
uint32_t sample_length_remainder;
/* Digital output? */
uint8_t digital;
/* Noise generator active? */
uint8_t noise;
uint8_t noise_shift_register;
/* Registers */
uint8_t plus4_sound_data[5];
};
int ted_sound_machine_init(struct plus4_sound_s* snd, int speed, int cycles_per_sec);
int ted_sound_machine_calculate_samples(struct plus4_sound_s* snd, int16_t *pbuf, int nr, int sound_chip_channels);
void ted_sound_machine_store(struct plus4_sound_s* snd, uint16_t addr, uint8_t val);
uint8_t ted_sound_machine_read(struct plus4_sound_s* snd, uint16_t addr);
void ted_sound_reset(struct plus4_sound_s* snd);
#ifdef __cplusplus
};
#endif
#endif

354
src/engine/platform/ted.cpp Normal file
View file

@ -0,0 +1,354 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 "ted.h"
#include "../engine.h"
#include <math.h>
//#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 8
const char* regCheatSheetTED[]={
"Freq0L", "0e",
"Freq1L", "0f",
"Freq1H", "10",
"Control", "11",
"Freq0H", "12",
NULL
};
const char** DivPlatformTED::getRegisterSheet() {
return regCheatSheetTED;
}
void DivPlatformTED::acquire(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
while (!writes.empty()) {
QueuedWrite w=writes.front();
ted_sound_machine_store(&ted,w.addr,w.val);
regPool[(w.addr-0x0e)&7]=w.val;
writes.pop();
}
ted_sound_machine_calculate_samples(&ted,&buf[0][h],1,1);
oscBuf[0]->data[oscBuf[0]->needle++]=(ted.voice0_output_enabled && ted.voice0_sign)?(ted.volume<<1):0;
oscBuf[1]->data[oscBuf[1]->needle++]=(ted.voice1_output_enabled && ((ted.noise && (!(ted.noise_shift_register&1))) || (!ted.noise && ted.voice1_sign)))?(ted.volume<<1):0;
}
}
void DivPlatformTED::tick(bool sysTick) {
bool resetPhase=false;
for (int _i=0; _i<2; _i++) {
int i=chanOrder[_i];
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol,MIN(8,chan[i].std.vol.val),8);
updateCtrl=true;
vol=chan[i].outVol;
}
if (chan[i].std.duty.had) {
chan[i].noise=chan[i].std.duty.val&2;
chan[i].square=chan[i].std.duty.val&1;
chan[i].freqChanged=true;
updateCtrl=true;
}
if (NEW_ARP_STRAT) {
chan[i].handleArp();
} else if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
}
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) {
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)-1;
if (i==1 && chan[i].noise && !chan[i].square) chan[i].freq>>=4;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>1023) chan[i].freq=1023;
if (i==1) {
rWrite(0x0f,(1022-chan[i].freq)&0xff);
rWrite(0x10,((1022-chan[i].freq)>>8)&0xff);
} else {
rWrite(0x0e,(1022-chan[i].freq)&0xff);
rWrite(0x12,((1022-chan[i].freq)>>8)&0xff);
}
if (chan[i].keyOn) {
updateCtrl=true;
}
if (chan[i].keyOff) {
updateCtrl=true;
}
if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false;
}
if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) {
resetPhase=true;
updateCtrl=true;
}
}
if (resetPhase) {
rWrite(0x11,0x80);
}
if (updateCtrl) {
updateCtrl=false;
rWrite(0x11,(vol&15)|((chan[0].active && chan[0].square && !isMuted[0])?0x10:0)|((chan[1].active && chan[1].square && !isMuted[1])?0x20:0)|((chan[1].active && chan[1].noise && !isMuted[1])?0x40:0));
}
}
int DivPlatformTED::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_TED);
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].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;
if (keyPriority) {
if (chanOrder[0]==c.chan) {
chanOrder[0]=chanOrder[1];
chanOrder[1]=c.chan;
}
}
break;
}
case DIV_CMD_NOTE_OFF:
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;
}
vol=chan[c.chan].outVol;
updateCtrl=true;
}
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].noise=c.value;
chan[c.chan].freqChanged=true;
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_TED));
}
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 8;
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_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformTED::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
updateCtrl=true;
}
void DivPlatformTED::forceIns() {
for (int i=0; i<2; i++) {
chan[i].freqChanged=true;
}
updateCtrl=true;
}
void* DivPlatformTED::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformTED::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformTED::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformTED::getRegisterPool() {
return regPool;
}
int DivPlatformTED::getRegisterPoolSize() {
return 5;
}
void DivPlatformTED::reset() {
writes.clear();
memset(regPool,0,8);
for (int i=0; i<2; i++) {
chan[i]=DivPlatformTED::Channel();
chan[i].std.setEngine(parent);
}
if (dumpWrites) {
addWrite(0xffffffff,0);
}
ted_sound_machine_init(&ted,1,8);
updateCtrl=true;
vol=15;
chanOrder[0]=0;
chanOrder[1]=1;
}
int DivPlatformTED::getOutputCount() {
return 1;
}
bool DivPlatformTED::keyOffAffectsArp(int ch) {
return true;
}
void DivPlatformTED::notifyInsDeletion(void* ins) {
for (int i=0; i<2; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformTED::setFlags(const DivConfig& flags) {
if (flags.getInt("clockSel",0)) {
chipClock=COLOR_PAL*2.0/5.0;
} else {
chipClock=COLOR_NTSC/2.0;
}
CHECK_CUSTOM_CLOCK;
rate=chipClock/8;
for (int i=0; i<2; i++) {
oscBuf[i]->rate=rate;
}
keyPriority=flags.getBool("keyPriority",true);
}
void DivPlatformTED::poke(unsigned int addr, unsigned short val) {
rWrite(addr,val);
}
void DivPlatformTED::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
}
int DivPlatformTED::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<2; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
setFlags(flags);
reset();
return 2;
}
void DivPlatformTED::quit() {
for (int i=0; i<2; i++) {
delete oscBuf[i];
}
}
DivPlatformTED::~DivPlatformTED() {
}

78
src/engine/platform/ted.h Normal file
View file

@ -0,0 +1,78 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 _TED_H
#define _TED_H
#include "../dispatch.h"
#include "../fixedQueue.h"
#include "sound/ted-sound.h"
class DivPlatformTED: public DivDispatch {
struct Channel: public SharedChannel<signed char> {
bool noise, square;
Channel():
SharedChannel<signed char>(8),
noise(false),
square(true) {}
};
Channel chan[2];
DivDispatchOscBuffer* oscBuf[2];
bool isMuted[2];
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
FixedQueue<QueuedWrite,64> writes;
struct plus4_sound_s ted;
unsigned char vol;
bool updateCtrl, keyPriority;
unsigned char chanOrder[2];
unsigned char regPool[8];
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<DivRegWrite>& wlist);
const char** getRegisterSheet();
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
~DivPlatformTED();
};
#endif

View file

@ -128,7 +128,8 @@ enum DivSystem {
DIV_SYSTEM_YM2608_CSM,
DIV_SYSTEM_SM8521,
DIV_SYSTEM_PV1000,
DIV_SYSTEM_K053260
DIV_SYSTEM_K053260,
DIV_SYSTEM_TED
};
enum DivEffectType: unsigned short {

View file

@ -1863,6 +1863,16 @@ void DivEngine::registerSystems() {
}
);
sysDefs[DIV_SYSTEM_TED]=new DivSysDef(
"MOS Technology TED", NULL, 0xcd, 0, 2, false, true, 0, false, 0,
"two square waves (one may be turned into noise). used in the Commodore Plus/4, 16 and 116.",
{"Channel 1", "Channel 2"},
{"CH1", "CH2"},
{DIV_CH_PULSE, DIV_CH_PULSE},
{DIV_INS_TED, DIV_INS_TED},
{}
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0,
"this is a system designed for testing purposes.",

View file

@ -182,6 +182,8 @@ const char* aboutLine[]={
"Stella by Stella Team",
"QSound emulator by superctr and Valley Bell",
"VICE VIC-20 sound core by Rami Rasanen and viznut",
"VICE TED sound core by Andreas Boose, Tibor Biczo",
"and Marco van den Heuvel",
"VERA sound core by Frank van den Hoef",
"mzpokeysnd POKEY emulator by Michael Borisov",
"ASAP POKEY emulator by Piotr Fusik",

View file

@ -282,6 +282,10 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K053260]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_TED:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TED]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i);

View file

@ -1420,6 +1420,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) {
prepareUndo(GUI_UNDO_CHANGE_ORDER);
e->lockSave([this,num]() {
if (!curNibble && !settings.pushNibble) e->curOrders->ord[orderCursor][curOrder]=0;
e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num);
});
MARK_MODIFIED;

View file

@ -240,6 +240,8 @@ enum FurnaceGUIColors {
GUI_COLOR_INSTR_SM8521,
GUI_COLOR_INSTR_PV1000,
GUI_COLOR_INSTR_K053260,
GUI_COLOR_INSTR_SCSP,
GUI_COLOR_INSTR_TED,
GUI_COLOR_INSTR_UNKNOWN,
GUI_COLOR_CHANNEL_BG,

View file

@ -168,6 +168,8 @@ const char* insTypes[DIV_INS_MAX+1]={
"SM8521",
"PV-1000",
"K053260",
"SCSP",
"TED",
NULL
};
@ -889,6 +891,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_INSTR_SM8521,"",ImVec4(0.5f,0.55f,0.6f,1.0f)),
D(GUI_COLOR_INSTR_PV1000,"",ImVec4(0.4f,0.6f,0.7f,1.0f)),
D(GUI_COLOR_INSTR_K053260,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
D(GUI_COLOR_INSTR_SCSP,"",ImVec4(0.5f,0.5f,0.5f,1.0f)),
D(GUI_COLOR_INSTR_TED,"",ImVec4(0.7f,0.6f,1.0f,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)),
@ -1073,6 +1077,7 @@ const int availableSystems[]={
DIV_SYSTEM_SM8521,
DIV_SYSTEM_PV1000,
DIV_SYSTEM_K053260,
DIV_SYSTEM_TED,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_PONG,
0 // don't remove this last one!
@ -1123,6 +1128,7 @@ const int chipsSquare[]={
DIV_SYSTEM_MSM5232,
DIV_SYSTEM_T6W28,
DIV_SYSTEM_PV1000,
DIV_SYSTEM_TED,
0 // don't remove this last one!
};

View file

@ -267,6 +267,10 @@ const char* msm5232ControlBits[7]={
"16'", "8'", "4'", "2'", "sustain", NULL
};
const char* tedControlBits[3]={
"square", "noise", NULL
};
const char* x1_010EnvBits[8]={
"enable", "oneshot", "split L/R", "HinvR", "VinvR", "HinvL", "VinvL", NULL
};
@ -5389,7 +5393,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_MSM6258) {
volMax=0;
}
if (ins->type==DIV_INS_MSM6295) {
if (ins->type==DIV_INS_MSM6295 || ins->type==DIV_INS_TED) {
volMax=8;
}
if (ins->type==DIV_INS_ADPCMA) {
@ -5475,6 +5479,10 @@ void FurnaceGUI::drawInsEdit() {
dutyLabel="On/Off";
dutyMax=1;
}
if (ins->type==DIV_INS_TED) {
dutyLabel="Square/Noise";
dutyMax=2;
}
if (ins->type==DIV_INS_SWAN) {
dutyLabel="Noise";
dutyMax=ins->amiga.useSample?0:8;
@ -5558,6 +5566,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_GA20) waveMax=0;
if (ins->type==DIV_INS_K053260) waveMax=0;
if (ins->type==DIV_INS_POKEMINI) waveMax=0;
if (ins->type==DIV_INS_TED) waveMax=0;
if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7;
if (ins->type==DIV_INS_PET) {
waveMax=8;
@ -5703,6 +5712,8 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits));
} else if (ins->type==DIV_INS_POKEY) {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,pokeyCtlBits));
} else if (ins->type==DIV_INS_TED) {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,80,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,tedControlBits));
} else if (ins->type==DIV_INS_MSM5232) {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,msm5232ControlBits));
} else if (ins->type==DIV_INS_ES5506) {
@ -5770,7 +5781,8 @@ void FurnaceGUI::drawInsEdit() {
(ins->type==DIV_INS_X1_010 && ins->amiga.useSample) ||
ins->type==DIV_INS_K007232 ||
ins->type==DIV_INS_GA20 ||
ins->type==DIV_INS_K053260) {
ins->type==DIV_INS_K053260 ||
ins->type==DIV_INS_TED) {
macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
}
if (ex1Max>0) {

View file

@ -278,6 +278,12 @@ void FurnaceGUI::initSystemPresets() {
ENTRY(
"Commodore VIC-20", {
CH(DIV_SYSTEM_VIC20, 1.0f, 0, "clockSel=1")
},
"tickRate=50"
);
ENTRY(
"Commodore Plus/4", {
CH(DIV_SYSTEM_TED, 1.0f, 0, "")
}
);
ENTRY(
@ -2433,6 +2439,11 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_PV1000, 1.0f, 0, "")
}
);
ENTRY(
"MOS Technology TED", {
CH(DIV_SYSTEM_TED, 1.0f, 0, "clockSel=1")
}
);
CATEGORY_END;
CATEGORY_BEGIN("Sample","chips/systems which use PCM or ADPCM samples for sound synthesis.");

View file

@ -1902,6 +1902,40 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
}
break;
}
case DIV_SYSTEM_TED: {
int clockSel=flags.getInt("clockSel",0);
bool keyPriority=flags.getBool("keyPriority",true);
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (1.79MHz)",clockSel==0)) {
clockSel=0;
altered=true;
}
if (ImGui::RadioButton("PAL (1.77MHz)",clockSel==1)) {
clockSel=1;
altered=true;
}
ImGui::Text("Global parameter priority:");
if (ImGui::RadioButton("Left to right",!keyPriority)) {
keyPriority=false;
altered=true;
}
if (ImGui::RadioButton("Last used channel",keyPriority)) {
keyPriority=true;
altered=true;
}
if (altered) {
e->lockSave([&]() {
flags.set("clockSel",clockSel);
flags.set("keyPriority",keyPriority);
});
}
break;
}
case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_PET:

View file

@ -191,6 +191,7 @@ TAParamResult pVersion(String) {
printf("- MAME SegaPCM core by Hiromitsu Shioya and Olivier Galibert (BSD 3-clause)\n");
printf("- QSound core by superctr (BSD 3-clause)\n");
printf("- VICE VIC-20 by Rami Rasanen and viznut (GPLv2)\n");
printf("- VICE TED by Andreas Boose, Tibor Biczo and Marco van den Heuvel (GPLv2)\n");
printf("- VERA core by Frank van den Hoef (BSD 2-clause)\n");
printf("- SAASound by Dave Hooper and Simon Owen (BSD 3-clause)\n");
printf("- SameBoy by Lior Halphon (MIT)\n");