A fork of the cSID music player by Hermit, currently aiming to improve the presentation of the source code.

[[ 🗃 ^wEGLr csid ]] :: [📥 Inbox] [📤 Outbox] [🐤 Followers] [🤝 Collaborators] [🛠 Changes]

Clone

HTTPS: darcs clone https://vervis.peers.community/repos/wEGLr

SSH: darcs clone USERNAME@vervis.peers.community:wEGLr

Tags

TODO

csid.c

// cSID by Hermit (Mihaly Horvath), (Year 2016..2017) http://hermit.sidrip.com
// (based on jsSID but totally revorked in C to be cycle-based & oversampled)
// License: WTF - Do what the fuck you want with this code, but I please mention me as its original author.

// This is a modified version of cSID, copyright (C) 2018 UltrasonicMadness

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <SDL/SDL_config.h>
#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>

typedef unsigned char byte;

// global constants and variables
#define C64_PAL_CPUCLK 985248
#define SID_CHANNEL_AMOUNT 3
#define MAX_DATA_LEN 65536
#define PAL_FRAMERATE 49.4 // important to match, otherwise some ADSR-sensitive tunes suffer.
#define DEFAULT_SAMPLERATE 44100

// raw output divided by this after multiplied by main volume, this also compensates for filter-resonance emphasis to avoid distotion
int OUTPUT_SCALEDOWN = SID_CHANNEL_AMOUNT * 16 + 26;

enum
{
    GATE_BITMASK=0x01, SYNC_BITMASK=0x02, RING_BITMASK=0x04,
    TEST_BITMASK=0x08, TRI_BITMASK=0x10, SAW_BITMASK=0x20,
    PULSE_BITMASK=0x40, NOISE_BITMASK=0x80, HOLDZERO_BITMASK=0x10,
    DECAYSUSTAIN_BITMASK=0x40, ATTACK_BITMASK=0x80, LOWPASS_BITMASK=0x10,
    BANDPASS_BITMASK=0x20, HIGHPASS_BITMASK=0x40, OFF3_BITMASK=0x80
};

const byte FILTSW[9] = {1, 2, 4, 1, 2, 4, 1, 2, 4};
byte ADSRstate[9], expcnt[9], envcnt[9], sourceMSBrise[9];
unsigned int clock_ratio = 22, ratecnt[9], prevwfout[9];
unsigned long int phaseaccu[9], prevaccu[9], sourceMSB[3], noise_LFSR[9];
long int prevlowpass[3], prevbandpass[3];
float cutoff_ratio_8580, cutoff_ratio_6581, cutoff_bias_6581;
int SIDamount = 1, SID_model[3] = {8580, 8580, 8580}, requested_SID_model = -1, sampleratio;
byte filedata[MAX_DATA_LEN], memory[MAX_DATA_LEN], timermode[0x20], SIDtitle[0x20], SIDauthor[0x20], SIDinfo[0x20];
int subtune = 0, tunelength = -1;
unsigned int initaddr, playaddr, playaddf, SID_address[3] = {0xD400, 0, 0};
long int samplerate = DEFAULT_SAMPLERATE;
int framecnt = 0, frame_sampleperiod = DEFAULT_SAMPLERATE / PAL_FRAMERATE;

// CPU (and CIA/VIC-IRQ) emulation constants and variables - avoiding internal/automatic variables to retain speed
const byte flagsw[] = {0x01, 0x21, 0x04, 0x24, 0x00, 0x40, 0x08, 0x28}, branchflag[] = {0x80, 0x40, 0x01, 0x02};
unsigned int PC = 0, pPC = 0, addr = 0, storadd = 0;
short int A = 0, T = 0, SP = 0xFF;

// STATUS-flags: N V - B D I Z C
byte X = 0, Y = 0, IR = 0, ST = 0x00;

char CPUtime = 0, cycles = 0, finished = 0, dynCIA = 0;

// function prototypes
void cSID_init(int samplerate);
int SID(char num, unsigned int baseaddr);
void initSID();
void initCPU(unsigned int mempos);

byte CPU();
void init(byte subtune);

void play(void* userdata, Uint8 *stream, int len);
unsigned int combinedWF(char num, char channel, unsigned int* wfarray, int index, char differ6581);
void createCombinedWF(unsigned int* wfarray, float bitmul, float bitstrength, float treshold);



//----------------------------- MAIN thread ----------------------------



int main(int argc, char *argv[])
{
    int readata, strend, subtune_amount, preferred_SID_model[3] = {8580.0, 8580.0, 8580.0};
    unsigned int i, datalen, offs, loadaddr;
    FILE *InputFile;
    
    // wait a bit to avoid keypress leftover (btw this might not happen in Linux)
    usleep(100000);
    
    // open and process the file
    if (argc < 2)
    {
        printf("\nUsage: csid <inputfile> [ subtune_number [SID_modelnumber [seconds]] ]\n\n");
        return 1;
    }
    
    if (argc >= 3)
    {
        sscanf(argv[2], "%d", &subtune);
        subtune--;
        
        if (subtune < 0 || subtune > 63)
            subtune = 0;
    }
    else
    {
        subtune = 0;
    }
    
    if (argc >= 4)
        sscanf(argv[3], "%d", &requested_SID_model);
    
    if (argc >= 5)
        sscanf(argv[4], "%d", &tunelength);
    
    InputFile = fopen(argv[1], "rb");
    
    if (InputFile == NULL)
    {
        printf("File not found.\n");
        return 1;
    }
    
    printf("\n");
    datalen = 0;
    
    do
    {
        readata = fgetc(InputFile);
        filedata[datalen++] = readata;
    }
    while (readata != EOF && datalen < MAX_DATA_LEN);
    
    printf("\n%d bytes read (%s subtune %d)", --datalen, argv[1], subtune + 1);
    fclose(InputFile);
    
    offs = filedata[7];
    loadaddr = filedata[8] + filedata[9] ? filedata[8] * 256 + filedata[9] : filedata[offs] + filedata[offs + 1] * 256;
    printf("\nOffset: $%4.4X, Loadaddress: $%4.4X \nTimermodes:", offs, loadaddr);
    
    for (i = 0; i < 32; i++)
    {
        timermode[31 - i] = (filedata[0x12 + (i >> 3)] & (byte)pow(2, 7 - i % 8)) ? 1 : 0;
        printf(" %1d", timermode[31 - i]);
    }
    
    for (i = 0; i < MAX_DATA_LEN; i++)
        memory[i] = 0;
    
    for (i = offs + 2; i < datalen; i++)
    {
        if (loadaddr + i - (offs + 2) < MAX_DATA_LEN)
            memory[loadaddr + i - (offs + 2)] = filedata[i];
    }
    
    strend = 1;
    for (i = 0; i < 32; i++)
    {
        if (strend != 0)
            strend = SIDtitle[i] = filedata[0x16 + i];
        else
            strend = SIDtitle[i] = 0;
    }
    
    printf("\nTitle: %s    ", SIDtitle);
    
    strend = 1;
    for (i = 0; i < 32; i++)
    {
        if (strend != 0)
            strend = SIDauthor[i] = filedata[0x36 + i];
        else
            strend = SIDauthor[i] = 0;
    }
    
    printf("Author: %s    ", SIDauthor);
    
    strend = 1;
    for (i = 0; i < 32; i++)
    {
        if (strend != 0)
            strend = SIDinfo[i] = filedata[0x56 + i];
        else
            strend = SIDinfo[i] = 0;
    }
    
    printf("Info: %s", SIDinfo);
    
    initaddr = filedata[0xA] + filedata[0xB] ? filedata[0xA] * 256 + filedata[0xB] : loadaddr;
    playaddr = playaddf = filedata[0xC] * 256 + filedata[0xD];
    printf("\nInit:$%4.4X,Play:$%4.4X, ", initaddr, playaddr);
    
    subtune_amount = filedata[0xF];
    preferred_SID_model[0] = (filedata[0x77] & 0x30) >= 0x20 ? 8580 : 6581;
    printf("Subtunes:%d , preferred SID-model:%d", subtune_amount, preferred_SID_model[0]);
    
    preferred_SID_model[1] = (filedata[0x77] & 0xC0) >= 0x80 ? 8580 : 6581;
    preferred_SID_model[2] = (filedata[0x76] & 3) >= 3 ? 8580 : 6581;
    
    SID_address[1] = filedata[0x7A] >= 0x42 && (filedata[0x7A] < 0x80 || filedata[0x7A] >= 0xE0) ? 0xD000 + filedata[0x7A] * 16 : 0;
    SID_address[2] = filedata[0x7B] >= 0x42 && (filedata[0x7B] < 0x80 || filedata[0x7B] >= 0xE0) ? 0xD000 + filedata[0x7B] * 16 : 0;
    
    SIDamount = 1 + (SID_address[1] > 0) + (SID_address[2] > 0);
    
    if (SIDamount >= 2)
        printf("(SID1), %d(SID2:%4.4X)", preferred_SID_model[1], SID_address[1]);
    
    if (SIDamount == 3)
        printf(", %d(SID3:%4.4X)", preferred_SID_model[2], SID_address[2]);
    
    if (requested_SID_model != -1)
        printf(" (requested:%d)", requested_SID_model);
    
    printf("\n");
    
    samplerate = DEFAULT_SAMPLERATE;
    sampleratio = round(C64_PAL_CPUCLK / samplerate);
    
    if (SDL_Init(SDL_INIT_AUDIO) < 0)
    {
        fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
        return(1);
    }
    
    SDL_AudioSpec soundspec;
    soundspec.freq = samplerate;
    soundspec.channels = 1;
    soundspec.format = AUDIO_S16;
    soundspec.samples = 16384;
    soundspec.userdata = NULL;
    soundspec.callback = play;
    
    if (SDL_OpenAudio(&soundspec, NULL) < 0)
    {
        fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
        return(2);
    }
    
    for (i = 0; i < SIDamount; i++)
    {
        if (requested_SID_model == 8580 || requested_SID_model == 6581)
            SID_model[i] = requested_SID_model;
        else
            SID_model[i] = preferred_SID_model[i];
    }
    
    if (SIDamount == 2)
        OUTPUT_SCALEDOWN /= 0.6;
    else if (SIDamount >= 3)
        OUTPUT_SCALEDOWN /= 0.4;
    
    cSID_init(samplerate);
    init(subtune);
    
    SDL_PauseAudio(0);
    
    fflush(stdin);
    
    if (tunelength != -1)
    {
        sleep(tunelength);
    }
    else
    {
        printf("Press Enter to abort playback...\n");
        getchar();
    }
    
    SDL_PauseAudio(1);
    SDL_CloseAudio();
    return 0;
}


void init(byte subt)
{
    static long int timeout;
    subtune = subt;
    initCPU(initaddr);
    initSID();
    A = subtune;
    memory[1] = 0x37;
    memory[0xDC05] = 0;
    
    for (timeout = 100000; timeout >= 0; timeout--)
    {
        if (CPU())
            break;
    }
    
    if (timermode[subtune] || memory[0xDC05]) // && playaddf {   //CIA timing
    {
        if (!memory[0xDC05])
        {
            // C64 startup-default
            memory[0xDC04] = 0x24;
            memory[0xDC05] = 0x40;
        }
        
        frame_sampleperiod = (memory[0xDC04] + memory[0xDC05] * 256) / clock_ratio;
    }
    else
    {
        // Vsync timing
        frame_sampleperiod = samplerate / PAL_FRAMERATE;
    }
    
    printf("Frame-sampleperiod: %d samples  (%.2fX speed)\n", frame_sampleperiod, (double)(samplerate / PAL_FRAMERATE) / frame_sampleperiod);
    // frame_sampleperiod = (memory[0xDC05] != 0 || (!timermode[subtune] && playaddf)) ? samplerate / PAL_FRAMERATE : (memory[0xDC04] + memory[0xDC05] * 256) / clock_ratio;
    
    if (playaddf == 0)
    {
        playaddr = ((memory[1] & 3) < 2) ? memory[0xFFFE] + memory[0xFFFF] * 256 : memory[0x314] + memory[0x315] * 256;
        printf("IRQ-playaddress:%4.4X\n", playaddr);
    }
    else
    {
        // player under KERNAL (Crystal Kingdom Dizzy)
        playaddr = playaddf;
        
        if (playaddr >= 0xE000 && memory[1] == 0x37)
            memory[1] = 0x35;
    }
    
    initCPU(playaddr);
    framecnt = 1;
    finished = 0;
    CPUtime = 0;
}


void play(void* userdata, Uint8 *stream, int len) // called by SDL at samplerate pace
{
    static int i, j, output;
    static float average;
    
    for (i = 0; i < len; i += 2)
    {
        framecnt--;
        
        if (framecnt <= 0)
        {
            framecnt = frame_sampleperiod;
            finished = 0;
            PC = playaddr;
            SP = 0xFF;
        }
        // printf("%d  %f\n",framecnt,playtime); }
        
        average = 0.0;
        
        for (j = 0; j < sampleratio; j++)
        {
            if (finished == 0 && --cycles <= 0)
            {
                pPC = PC;
                
                if (CPU() >= 0xFE || ((memory[1] & 3) > 1 && pPC < 0xE000 && (PC == 0xEA31 || PC == 0xEA81)))
                    finished = 1; // IRQ player ROM return handling
                    
                if ((addr == 0xDC05 || addr == 0xDC04) && (memory[1] & 3) && timermode[subtune])
                {
                    // dynamic CIA-setting (Galway/Rubicon workaround)
                    frame_sampleperiod = (memory[0xDC04] + memory[0xDC05] * 256) / clock_ratio;
                    
                    if (!dynCIA)
                    {
                        dynCIA = 1;
                        printf("( Dynamic CIA settings. New frame-sampleperiod: %d samples  (%.2fX speed) )\n", frame_sampleperiod, (double)(samplerate / PAL_FRAMERATE) / frame_sampleperiod);
                    }
                }
                
                if (storadd >= 0xD420 && storadd < 0xD800 && (memory[1] & 3))
                {
                    // CJ in the USA workaround (writing above $d420, except SID2/SID3)
                    if (!(SID_address[1] <= storadd && storadd < SID_address[1] + 0x1F) && !(SID_address[2] <= storadd && storadd < SID_address[2] + 0x1F))
                        memory[storadd & 0xD41F] = memory[storadd]; // write to $D400..D41F if not in SID2/SID3 address-space
                }
            }
            
            average += SID(0, 0xD400);
            
            if (SIDamount >= 2)
                average += SID(1, SID_address[1]);
            
            if (SIDamount == 3)
                average += SID(2, SID_address[2]);
        }
        
        output = average / sampleratio;
        stream[i] = output & 0xFF;
        stream[i + 1] = output >> 8;
    }
    
    /* 
     * mix = SID(0,0xD400);
     * if (SID_address[1])
     *     mix += SID(1, SID_address[1]);
     *
     * if (SID_address[2])
     *     mix += SID(2, SID_address[2]);
     * 
     * return mix * volume * SIDamount_vol[SIDamount] + (Math.random() * background_noise - background_noise / 2);
     */
}



//--------------------------------- CPU emulation -------------------------------------------



void initCPU(unsigned int mempos)
{
    PC = mempos;
    A = 0;
    X = 0;
    Y = 0;
    ST = 0;
    SP = 0xFF;
}


byte CPU() // the CPU emulation for SID/PRG playback (ToDo: CIA/VIC-IRQ/NMI/RESET vectors, BCD-mode)
{
    // 'IR' is the instruction-register, naming after the hardware-equivalent
    IR = memory[PC];
    
    // 'cycle': ensure smallest 6510 runtime (for implied/register instructions)
    cycles = 2;
    
    storadd = 0;
    
    if (IR & 1) // nybble2:  1/5/9/D:accu.instructions, 3/7/B/F:illegal opcodes
    {
        switch (IR & 0x1F) // addressing modes (begin with more complex cases), PC wraparound not handled inside to save codespace
        {
            case 1:
            case 3:
                // (zp,x)
                addr = memory[memory[++PC] + X] + memory[memory[PC] + X + 1] * 256;
                cycles = 6;
                break;
            
            case 0x11:
            case 0x13:
                // (zp),y
                addr = memory[memory[++PC]] + memory[memory[PC] + 1] * 256 + Y;
                cycles = 6;
                break;
                
            case 0x19:
            case 0x1B:
                // abs,y
                addr = memory[++PC] + memory[++PC] * 256 + Y;
                cycles = 5;
                break;
                
            case 0x1D:
                // abs,x
                addr = memory[++PC] + memory[++PC] * 256 + X;
                cycles = 5;
                break;
            
            case 0xD:
            case 0xF:
                // abs
                addr = memory[++PC] + memory[++PC] * 256;
                cycles = 4;
                break;
                
            case 0x15:
                // zp,x
                addr = memory[++PC] + X;
                cycles = 4;
                break;
                
            case 5:
            case 7:
                // zp
                addr = memory[++PC];
                cycles = 3;
                break;
                
            case 0x17:
                if ((IR & 0xC0) != 0x80)
                {
                    // zp,x for illegal opcodes
                    addr = memory[++PC] + X;
                    cycles = 4;
                }
                else
                {
                    // zp,y for LAX/SAX illegal opcodes
                    addr = memory[++PC] + Y;
                    cycles = 4;
                }
                
                break;
                
            case 0x1F:
                if ((IR & 0xC0) != 0x80)
                {
                    // abs,x for illegal opcodes
                    addr = memory[++PC] + memory[++PC] * 256 + X;
                    cycles = 5;
                }
                else
                {
                    // abs,y for LAX/SAX illegal opcodes
                    addr = memory[++PC] + memory[++PC] * 256 + Y;
                    cycles = 5;
                }
                
                break;
                
            case 9:
            case 0xB:
                // immediate
                addr = ++PC;
                cycles = 2;
        }
        
        addr &= 0xFFFF;
        
        switch (IR & 0xE0)
        {
            case 0x60:
                if ((IR & 0x1F) != 0xB)
                {
                    if ((IR & 3) == 3)
                    {
                        // ADC / RRA (ROR+ADC)
                        T = (memory[addr] >> 1) + (ST & 1) * 128;
                        ST &= 124;
                        ST |= (T & 1);
                        memory[addr] = T;
                        cycles += 2;
                    }
                    
                    T = A;
                    A += memory[addr] + (ST & 1);
                    ST &= 60;
                    ST |= (A & 128) | (A > 255);
                    A &= 0xFF;
                    ST |= (!A) << 1 | (!((T ^ memory[addr]) & 0x80) & ((T ^ A) & 0x80)) >> 1;
                }
                else
                {
                    // V-flag set by intermediate ADC mechanism: (A&mem)+mem
                    A &= memory[addr];
                    T += memory[addr] + (ST & 1);
                    ST &= 60;
                    ST |= (T > 255) | (!((A ^ memory[addr]) & 0x80) & ((T ^ A) & 0x80)) >> 1;
                    
                    // ARR (AND+ROR, bit0 not going to C, but C and bit7 get exchanged.)
                    T = A;
                    A = (A >> 1) + (ST & 1) * 128;
                    ST |= (A & 128) | (T > 127);
                    ST |= (!A) << 1;
                }
                
                break;
            
            case 0xE0:
                // SBC / ISC(ISB)=INC+SBC
                if ((IR & 3) == 3 && (IR & 0x1F) != 0xB)
                {
                    memory[addr]++;
                    cycles += 2;
                }
                
                T = A;
                A -= memory[addr] + !(ST & 1);
                
                ST &= 60;
                ST |= (A & 128) | (A >= 0);
                A &= 0xFF;
                ST |= (!A) << 1 | (((T ^ memory[addr]) & 0x80) & ((T ^ A) & 0x80)) >> 1;
                
                break;
                
            case 0xC0:
                if((IR & 0x1F) != 0xB)
                {
                    // CMP / DCP(DEC+CMP)
                    if ((IR & 3) == 3)
                    {
                        memory[addr]--;
                        cycles += 2;
                    }
                    
                    T = A - memory[addr];
                }
                else
                {
                    // SBX(AXS)
                    X = T = (A & X) - memory[addr];
                }
                
                // SBX (AXS) (CMP+DEX at the same time)
                ST &= 124;
                ST |= (!(T & 0xFF)) << 1 | (T & 128) | (T >= 0);
                
                break;
                
            case 0x00:
                if ((IR & 0x1F) != 0xB)
                {
                    if ((IR & 3) == 3)
                    {
                        ST &= 124;
                        ST |= (memory[addr] > 127);
                        memory[addr] <<= 1;
                        cycles += 2;
                    }
                    
                    // ORA / SLO(ASO)=ASL+ORA
                    A |= memory[addr];
                    ST &= 125;
                    ST |= (!A) << 1 | (A & 128);
                }
                else
                {
                    // ANC (AND+Carry=bit7)
                    A &= memory[addr];
                    ST &= 124;
                    ST |= (!A) << 1 | (A & 128) | (A > 127);
                }
                
                break;
            
            case 0x20:
                if ((IR & 0x1F) != 0xB)
                {
                    if ((IR & 3) == 3)
                    {
                        T = (memory[addr] << 1) + (ST & 1);
                        ST &= 124;
                        ST |= (T > 255);
                        T &= 0xFF;
                        memory[addr] = T;
                        cycles += 2;
                    }
                    
                    // AND / RLA (ROL+AND)
                    A &= memory[addr];
                    ST &= 125;
                    ST |= (!A) << 1 | (A & 128);
                }
                else
                {
                    // ANC (AND+Carry=bit7)
                    A &= memory[addr];
                    ST &= 124;
                    ST |= (!A) << 1 | (A & 128) | (A > 127);
                }
                
                break;
                
            case 0x40:
                if ((IR & 0x1F) != 0xB)
                {
                    if ((IR & 3) == 3)
                    {
                        ST &= 124;
                        ST |= (memory[addr] & 1);
                        memory[addr] >>= 1;
                        cycles += 2;
                    }
                    
                    // EOR / SRE(LSE)=LSR+EOR
                    A ^= memory[addr];
                    ST &= 125;
                    ST |= (!A) << 1 | (A & 128);
                }
                else
                {
                    // ALR(ASR)=(AND+LSR)
                    A &= memory[addr];
                    ST &= 124;
                    ST |= (A & 1);
                    A >>= 1;
                    A &= 0xFF;
                    ST |= (A & 128) | ((!A) << 1);
                }
                
                break;
                
            case 0xA0:
                if ((IR & 0x1F) != 0x1B)
                {
                    // LDA / LAX (illegal, used by my 1 rasterline player)
                    A = memory[addr];
                    
                    if ((IR & 3) == 3)
                        X = A;
                }
                else
                {
                    // LAS(LAR)
                    A = X = SP = memory[addr] & SP;
                }
                
                ST &= 125;
                ST |= ((!A) << 1) | (A & 128);
                
                break;
            
            case 0x80:
                if ((IR & 0x1F) == 0xB)
                {
                    // XAA (TXA+AND), highly unstable on real 6502!
                    A = X & memory[addr];
                    ST &= 125;
                    ST |= (A & 128) | ((!A) << 1);
                }
                else if ((IR & 0x1F) == 0x1B)
                {
                    // TAS(SHS) (SP=A&X, mem=S&H} - unstable on real 6502
                    SP = A & X;
                    memory[addr] = SP & ((addr >> 8) + 1);
                }
                else
                {
                    // STA / SAX (at times same as AHX/SHX/SHY) (illegal)
                    memory[addr] = A & (((IR & 3) == 3) ? X : 0xFF);
                    storadd = addr;
                }
                
                break;
        }
    }
    else if (IR & 2) // nybble2:  2:illegal/LDX, 6:A/X/INC/DEC, A:Accu-shift/reg.transfer/NOP, E:shift/X/INC/DEC
    {
        switch (IR & 0x1F) // addressing modes
        {
            case 0x1E:
                // abs,x / abs,y
                addr = memory[++PC] + memory[++PC] * 256 + (((IR & 0xC0) != 0x80) ? X : Y);
                cycles = 5;
                break;
            
            case 0xE:
                // abs
                addr = memory[++PC] + memory[++PC] * 256;
                cycles = 4;
                break;
            
            case 0x16:
                // zp,x / zp,y
                addr = memory[++PC] + (((IR & 0xC0) != 0x80) ? X : Y);
                cycles = 4;
                break;
            
            case 6:
                // zp
                addr = memory[++PC];
                cycles = 3;
                break;
            
            case 2:
                // imm.
                addr = ++PC;
                cycles = 2;
        }
        
        addr &= 0xFFFF;
        
        switch (IR & 0xE0)
        {
            case 0x00:
                ST &= 0xFE;
            case 0x20:
                if ((IR & 0xF) == 0xA)
                {
                    // ASL/ROL (Accu)
                    A = (A << 1) + (ST & 1);
                    ST &= 124;
                    ST |= (A & 128) | (A > 255);
                    A &= 0xFF;
                    ST |= (!A) << 1;
                }
                else
                {
                    // RMW (Read-Write-Modify)
                    T = (memory[addr] << 1) + (ST & 1);
                    ST &= 124;
                    ST |= (T & 128) | (T > 255);
                    T &= 0xFF;
                    ST |= (!T) << 1;
                    memory[addr] = T;
                    cycles += 2;
                }
                
                break;
            
            case 0x40:
                ST &= 0xFE;
            case 0x60:
                if ((IR & 0xF) == 0xA)
                {
                    // LSR/ROR (Accu)
                    T = A;
                    A = (A >> 1) + (ST & 1) * 128;
                    ST &= 124;
                    ST |= (A & 128) | (T & 1);
                    A &= 0xFF;
                    ST |= (!A) << 1;
                }
                else
                {
                    // memory (RMW)
                    T = (memory[addr] >> 1) + (ST & 1) * 128;
                    ST &= 124;
                    ST |= (T & 128) | (memory[addr] & 1);
                    T &= 0xFF;
                    ST |= (!T) << 1;
                    memory[addr] = T;
                    cycles += 2;
                }
                
                break;
            
            case 0xC0:
                if (IR & 4)
                {
                    // DEC
                    memory[addr]--;
                    ST &= 125;
                    ST |= (!memory[addr]) << 1 | (memory[addr] & 128);
                    cycles += 2;
                }
                else
                {
                    // DEX
                    X--;
                    X &= 0xFF;
                    ST &= 125;
                    ST |= (!X) << 1 | (X & 128);
                }
                
                break;
            
            case 0xA0:
                // LDX/TSX/TAX
                if ((IR & 0xF) != 0xA)
                {
                    X = memory[addr];
                }
                else if (IR & 0x10)
                {
                    X = SP;
                    break;
                }
                else
                {
                    X = A;
                }
                
                ST &= 125;
                ST |= (!X) << 1 | (X & 128);
                
                break;
            
            case 0x80:
                // STX/TXS/TXA
                if (IR & 4)
                {
                    memory[addr] = X;
                    storadd = addr;
                }
                else if (IR & 0x10)
                {
                    SP = X;
                }
                else
                {
                    A = X;
                    ST &= 125;
                    ST |= (!A) << 1 | (A & 128);
                }
                
                break;
            
            case 0xE0:
                // INC/NOP
                if (IR & 4)
                {
                    memory[addr]++;
                    ST &= 125;
                    ST |= (!memory[addr]) << 1 | (memory[addr] & 128);
                    cycles += 2;
                }
            
        }
    }
    else if ((IR & 0xC) == 8) // nybble2:  8:register/status
    {
        switch (IR & 0xF0)
        {
            case 0x60:
                // PLA
                SP++;
                SP &= 0xFF;
                A = memory[0x100 + SP];
                ST &= 125;
                ST |= (!A) << 1 | (A & 128);
                cycles = 4;
                break;
            
            case 0xC0:
                // INY
                Y++;
                Y &= 0xFF;
                ST &= 125;
                ST |= (!Y) << 1 | (Y & 128);
                break;
            
            case 0xE0:
                // INX
                X++;
                X &= 0xFF;
                ST &= 125;
                ST |= (!X) << 1 | (X & 128);
                break;
            
            case 0x80:
                // DEY
                Y--;
                Y &= 0xFF;
                ST &= 125;
                ST |= (!Y) << 1 | (Y & 128);
                break;
                
            case 0x00:
                // PHP
                memory[0x100 + SP] = ST;
                SP--;
                SP &= 0xFF;
                cycles = 3;
                break;
            
            case 0x20:
                // PLP
                SP++;
                SP &= 0xFF;
                ST = memory[0x100 + SP];
                cycles = 4;
                break;
            
            case 0x40:
                // PHA
                memory[0x100 + SP] = A;
                SP--;
                SP &= 0xFF;
                cycles = 3;
                break;
            
            case 0x90:
                // TYA
                A = Y;
                ST &= 125;
                ST |= (!A) << 1 | (A & 128);
                break;
            
            case 0xA0:
                // TAY
                Y = A;
                ST &= 125;
                ST |= (!Y) << 1 | (Y & 128);
                break;
            
            default:
                // CLC/SEC/CLI/SEI/CLV/CLD/SED
                if (flagsw[IR >> 5] & 0x20)
                    ST |= (flagsw[IR >> 5] & 0xDF);
                else
                    ST &= 255 - (flagsw[IR >> 5] & 0xDF);
        }
    }
    else // nybble2:  0: control/branch/Y/compare  4: Y/compare  C:Y/compare/JMP
    {
        if ((IR & 0x1F) == 0x10)
        {
            // BPL/BMI/BVC/BVS/BCC/BCS/BNE/BEQ  relative branch
            PC++;
            T = memory[PC];
            
            if (T & 0x80)
                T -= 0x100;
            
            if (IR & 0x20)
            {
                if (ST & branchflag[IR >> 6])
                {
                    PC += T;
                    cycles = 3;
                }
            }
            else
            {
                if (!(ST & branchflag[IR >> 6]))
                {
                    PC += T;
                    cycles = 3;
                }
            }
        }
        else // nybble2:  0:Y/control/Y/compare  4:Y/compare  C:Y/compare/JMP
        {
            switch (IR & 0x1F) // addressing modes
            {
                case 0:
                    // imm. (or abs.low for JSR/BRK)
                    addr = ++PC;
                    cycles = 2;
                    break;
                
                case 0x1C:
                    // abs,x
                    addr = memory[++PC] + memory[++PC] * 256 + X;
                    cycles = 5;
                    break;
                
                case 0xC:
                    // abs
                    addr = memory[++PC] + memory[++PC] * 256;
                    cycles = 4;
                    break;
                
                case 0x14:
                    // zp,x
                    addr = memory[++PC] + X;
                    cycles = 4;
                    break;
                
                case 4:
                    // zp
                    addr = memory[++PC];
                    cycles = 3;
            }
            
            addr &= 0xFFFF;
            
            switch (IR & 0xE0)
            {
                case 0x00:
                    // BRK
                    memory[0x100 + SP] = PC % 256;
                    SP--;
                    SP &= 0xFF;
                    memory[0x100 + SP] = PC / 256;
                    
                    SP--;
                    SP &= 0xFF;
                    memory[0x100 + SP] = ST;
                    SP--;
                    SP &= 0xFF;
                    
                    PC = memory[0xFFFE] + memory[0xFFFF] * 256 - 1;
                    cycles = 7;
                    break;
                
                case 0x20:
                    if (IR & 0xF)
                    {
                        // BIT
                        ST &= 0x3D;
                        ST |= (memory[addr] & 0xC0) | (!(A & memory[addr])) << 1;
                    }
                    else
                    {
                        // JSR
                        memory[0x100 + SP] = (PC + 2) % 256;
                        SP--;
                        SP &= 0xFF;
                        memory[0x100 + SP] = (PC + 2) / 256;
                        
                        SP--;
                        SP &= 0xFF;
                        PC = memory[addr] + memory[addr + 1] * 256 - 1;
                        cycles = 6;
                    }
                    
                    break;
                
                case 0x40:
                    if (IR & 0xF)
                    {
                        // JMP
                        PC = addr - 1;
                        cycles = 3;
                    }
                    else
                    {
                        // RTI
                        if (SP >= 0xFF)
                            return 0xFE;
                        
                        SP++;
                        SP &= 0xFF;
                        ST = memory[0x100 + SP];
                        SP++;
                        SP &= 0xFF;
                        T = memory[0x100 + SP];
                        SP++;
                        SP &= 0xFF;
                        PC = memory[0x100 + SP] + T * 256 - 1;
                        cycles = 6;
                    }
                    
                    break;
                
                case 0x60:
                    if (IR & 0xF)
                    {
                        // JMP() (indirect)
                        PC = memory[addr] + memory[addr + 1] * 256 - 1;
                        cycles = 5;
                    }
                    else
                    {
                        // RTS
                        if (SP >= 0xFF)
                            return 0xFF;
                        
                        SP++;
                        SP &= 0xFF;
                        T = memory[0x100 + SP];
                        SP++;
                        SP &= 0xFF;
                        PC = memory[0x100 + SP] + T * 256 - 1;
                        cycles = 6;
                    }
                    
                    break;
                
                case 0xC0:
                    // CPY
                    T = Y - memory[addr];
                    ST &= 124;
                    ST |= (!(T & 0xFF)) << 1 | (T & 128) | (T >= 0);
                    break;
                
                case 0xE0:
                    // CPX
                    T = X - memory[addr];
                    ST &= 124;
                    ST |= (!(T & 0xFF)) << 1 | (T & 128) | (T >= 0);
                    break;
                
                case 0xA0:
                    // LDY
                    Y = memory[addr];
                    ST &= 125;
                    ST |= (!Y) << 1 | (Y & 128);
                    break;
                
                case 0x80:
                    // STY
                    memory[addr] = Y;
                    storadd = addr;
            }
        }
    }

    // if(IR==0xCB) //test SBX
    // printf("PC:%4.4X IR:%2.2X, addr: %4.4X,%2.2X,  storadd: %4.4X,%2.2X,  A:%2.2X, X:%2.2X, Y:%2.2X ST:%2.2X\n",PC,IR,addr,memory[addr],storadd,memory[storadd],A,X,Y,ST);
    
    PC++; // PC &= 0xFFFF;
    return 0;
}



//----------------------------- SID emulation -----------------------------------------



unsigned int TriSaw_8580[4096], PulseSaw_8580[4096], PulseTriSaw_8580[4096];
int ADSRperiods[16] = {9, 32, 63, 95, 149, 220, 267, 313, 392, 977, 1954, 3126, 3907, 11720, 19532, 31251};

const byte ADSR_exptable[256] = {1, 30, 30, 30, 30, 30, 30, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, //pos0:1  pos6:30  pos14:16  pos26:8
    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, //pos54:4 //pos93:2
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };


void cSID_init(int samplerate)
{
    int i;
    clock_ratio = round(C64_PAL_CPUCLK / samplerate);
    cutoff_ratio_8580 = -2 * 3.14 * (12500.0 / 2048) / C64_PAL_CPUCLK;
    cutoff_ratio_6581 = -2 * 3.14 * (20000.0 / 2048) / C64_PAL_CPUCLK;
    
    // around 220Hz below treshold
    cutoff_bias_6581 = 1 - exp(-2 * 3.14 * 220 / C64_PAL_CPUCLK);
    
    createCombinedWF(TriSaw_8580, 0.8, 2.4, 0.64);
    createCombinedWF(PulseSaw_8580, 1.4, 1.9, 0.68);
    createCombinedWF(PulseTriSaw_8580, 0.8, 2.5, 0.64);
    
    for (i = 0; i < 9; i++)
    {
        ADSRstate[i] = HOLDZERO_BITMASK;
        envcnt[i] = 0;
        ratecnt[i] = 0;
        
        phaseaccu[i] = 0;
        prevaccu[i] = 0;
        expcnt[i] = 0;
        
        noise_LFSR[i] = 0x7FFFF8;
        prevwfout[i] = 0;
    }
    
    for (i = 0; i < 3; i++)
    {
        sourceMSBrise[i] = 0;
        sourceMSB[i] = 0;
        
        prevlowpass[i] = 0;
        prevbandpass[i] = 0;
    }
    
    initSID();
}


void initSID()
{
    int i;
    
    for (i = 0xD400; i <= 0xD7FF; i++)
        memory[i] = 0;
    
    for (i = 0xDE00; i <= 0xDFFF; i++)
        memory[i]=0;
    
    for (i = 0; i < 9; i++)
    {
        ADSRstate[i] = HOLDZERO_BITMASK;
        ratecnt[i] = envcnt[i] = expcnt[i] = 0;
    }
}


int SID(char num, unsigned int baseaddr)
{
    // better keep these variables static so they won't slow down the routine like if they were internal automatic variables always recreated
    static byte channel, ctrl, SR, prevgate, wf, test, filterctrl_prescaler[3];
    static byte *sReg, *vReg;
    static unsigned int period, accuadd, pw, wfout;
    static unsigned long int MSB;
    
    // cutoff must be signed otherwise compiler may make errors in multiplications
    static int nonfilt, filtin, cutoff[3], resonance[3];
    
    // so if samplerate is smaller, cutoff needs to be 'long int' as its value can exceed 32768
    static long int output, filtout, ftmp;

    filtin = nonfilt = 0;
    sReg = &memory[baseaddr];
    vReg = sReg;
    
    for (channel = num * SID_CHANNEL_AMOUNT; channel < (num + 1) * SID_CHANNEL_AMOUNT; channel++, vReg += 7)
    {
        ctrl = vReg[4];
        
        // ADSR envelope generator:
        {
            SR = vReg[6];
            prevgate = (ADSRstate[channel] & GATE_BITMASK);
            
            // gatebit-change?
            if (prevgate != (ctrl & GATE_BITMASK))
            {
                if (prevgate) // falling edge
                    ADSRstate[channel] &= 0xFF - (GATE_BITMASK | ATTACK_BITMASK | DECAYSUSTAIN_BITMASK);
                else // rising edge, also sets hold_zero_bit=0
                    ADSRstate[channel] = (GATE_BITMASK | ATTACK_BITMASK | DECAYSUSTAIN_BITMASK);
            }
            
            if (ADSRstate[channel] & ATTACK_BITMASK)
                period = ADSRperiods[vReg[5] >> 4];
            else if (ADSRstate[channel] & DECAYSUSTAIN_BITMASK)
                period = ADSRperiods[vReg[5] & 0xF];
            else
                period = ADSRperiods[SR & 0xF];
            
            // can wrap around (ADSR delay-bug: short 1st frame)
            ratecnt[channel]++;
            ratecnt[channel] &= 0x7FFF;
            
            if (ratecnt[channel] == period) // ratecounter shot (matches rateperiod) (in genuine SID ratecounter is LFSR)
            {
                // reset rate-counter on period-match
                ratecnt[channel] = 0;
                
                if ((ADSRstate[channel] & ATTACK_BITMASK) || ++expcnt[channel] == ADSR_exptable[envcnt[channel]])
                {
                    expcnt[channel] = 0;
                    
                    if (!(ADSRstate[channel] & HOLDZERO_BITMASK))
                    {
                        if (ADSRstate[channel] & ATTACK_BITMASK)
                        {
                            envcnt[channel]++;
                            
                            if (envcnt[channel] == 0xFF)
                                ADSRstate[channel] &= 0xFF - ATTACK_BITMASK;
                        }
                        else if (!(ADSRstate[channel] & DECAYSUSTAIN_BITMASK) || envcnt[channel] != (SR >> 4) + (SR & 0xF0))
                        {
                            // resid adds 1 cycle delay, we omit that pipelining mechanism here
                            envcnt[channel]--;
                            
                            if (envcnt[channel] == 0)
                                ADSRstate[channel] |= HOLDZERO_BITMASK;
                        }
                    }
                }
            }
        }
        
        // WAVE generation codes (phase accumulator and waveform-selector):
        test = ctrl & TEST_BITMASK;
        wf = ctrl & 0xF0;
        accuadd = (vReg[0] + vReg[1] * 256);
        
        if (test || ((ctrl & SYNC_BITMASK) && sourceMSBrise[num]))
        {
            phaseaccu[channel] = 0;
        }
        else
        {
            phaseaccu[channel] += accuadd;
            phaseaccu[channel] &= 0xFFFFFF;
        }
        
        MSB = phaseaccu[channel] & 0x800000;
        sourceMSBrise[num] = (MSB > (prevaccu[channel] & 0x800000)) ? 1 : 0;
        
        if (wf & NOISE_BITMASK)
        {
            int tmp = noise_LFSR[channel];
            
            if (((phaseaccu[channel] & 0x100000) != (prevaccu[channel] & 0x100000)))
            {
                int step = (tmp & 0x400000) ^ ((tmp & 0x20000) << 5);
                tmp = ((tmp << 1) + (step ? 1 : test)) & 0x7FFFFF;
                noise_LFSR[channel] = tmp;
            }
            
            wfout = (wf & 0x70) ? 0 : ((tmp & 0x100000) >> 5) + ((tmp & 0x40000) >> 4) + ((tmp & 0x4000) >> 1) + ((tmp & 0x800) << 1) + ((tmp & 0x200) << 2) + ((tmp & 0x20) << 5) + ((tmp & 0x04) << 7) + ((tmp & 0x01) << 8);
        }
        else if (wf & PULSE_BITMASK)
        {
            pw = (vReg[2] + (vReg[3] & 0xF) * 256) * 16;
            
            int tmp = phaseaccu[channel] >> 8;
            
            if (wf == PULSE_BITMASK)
            {
                if (test || tmp >= pw)
                    wfout = 0xFFFF;
                else
                    wfout = 0;
            }
            else // combined pulse
            {
                wfout = (tmp >= pw || test) ? 0xFFFF : 0;
                
                if (wf & TRI_BITMASK) // pulse+triangle
                {
                    if (wf & SAW_BITMASK) // pulse+saw+triangle (waveform nearly identical to tri+saw)
                    {
                        wfout = (wfout) ? combinedWF(num, channel, PulseTriSaw_8580, tmp >> 4, 1) : 0;
                    }
                    else
                    {
                        tmp = phaseaccu[channel] ^ (ctrl & RING_BITMASK ? sourceMSB[num] : 0);
                        wfout = (wfout) ? combinedWF(num, channel, PulseSaw_8580, (tmp ^ (tmp & 0x800000 ? 0xFFFFFF : 0)) >> 11, 0) : 0;
                    }
                }
                else if (wf & SAW_BITMASK) // pulse+saw
                    wfout = (wfout) ? combinedWF(num, channel, PulseSaw_8580, tmp >> 4, 1) : 0;
            }
        }
        else if (wf & SAW_BITMASK)
        {
            // saw
            wfout = phaseaccu[channel] >> 8;
            
            if (wf & TRI_BITMASK) // saw+triangle
                wfout = combinedWF(num, channel, TriSaw_8580, wfout >> 4, 1);
        }
        else if (wf & TRI_BITMASK)
        {
            int tmp = phaseaccu[channel] ^ (ctrl & RING_BITMASK ? sourceMSB[num] : 0);
            wfout = (tmp ^ (tmp & 0x800000 ? 0xFFFFFF : 0)) >> 7;
        }
        
        if (wf)
            prevwfout[channel] = wfout;
        else // emulate waveform 00 floating wave-DAC
            wfout = prevwfout[channel];
        
        prevaccu[channel] = phaseaccu[channel];
        sourceMSB[num] = MSB;
        
        if (sReg[0x17] & FILTSW[channel])
            filtin += ((long int)wfout - 0x8000) * envcnt[channel] / 256;
        else if ((FILTSW[channel] != 4) || !(sReg[0x18] & OFF3_BITMASK))
            nonfilt += ((long int)wfout - 0x8000) * envcnt[channel] / 256;
    }
    
    // update readable SID1-registers (some SID tunes might use 3rd channel ENV3/OSC3 value as control)
    if (num == 0, memory[1] & 3) // OSC3, ENV3 (some players rely on it)
    {
        sReg[0x1B] = wfout >> 8;
        sReg[0x1C] = envcnt[3];
    }
    
    // FILTER:
    filterctrl_prescaler[num]--;
    if (filterctrl_prescaler[num] == 0)
    {
        // calculate cutoff and resonance curves only at samplerate is still adequate and reduces CPU stress of frequent float calculations
        filterctrl_prescaler[num] = clock_ratio;
        cutoff[num] = 2 + sReg[0x16] * 8 + (sReg[0x15] & 7);
        
        if (SID_model[num] == 8580)
        {
            cutoff[num] = (1 - exp(cutoff[num] * cutoff_ratio_8580)) * 0x10000;
            
            // resonance could be taken from table as well
            resonance[num] = (pow(2, ((4 - (sReg[0x17] >> 4)) / 8.0))) * 0x100;
        }
        else
        {
            cutoff[num] = (cutoff_bias_6581 + ((cutoff[num] < 192) ? 0 : 1 - exp((cutoff[num] - 192) * cutoff_ratio_6581))) * 0x10000;
            resonance[num] = ((sReg[0x17] > 0x5F) ? 8.0 / (sReg[0x17] >> 4) : 1.41) * 0x100;
        }
    }
    
    // the filter-calculation itself can't be prescaled because sound-quality would suffer of no 'oversampling'
    filtout = 0;
    
    ftmp = filtin + prevbandpass[num] * resonance[num] / 0x100 + prevlowpass[num];
    
    if (sReg[0x18] & HIGHPASS_BITMASK)
        filtout -= ftmp;
    
    ftmp = prevbandpass[num] - ftmp * cutoff[num] / 0x10000;
    prevbandpass[num] = ftmp;
    
    if (sReg[0x18] & BANDPASS_BITMASK)
        filtout -= ftmp;
    
    ftmp = prevlowpass[num] + ftmp * cutoff[num] / 0x10000;
    prevlowpass[num] = ftmp;
    
    if (sReg[0x18] & LOWPASS_BITMASK)
        filtout += ftmp;
    
    // output stage for one SID
    output = (nonfilt + filtout) * (sReg[0x18] & 0xF) / OUTPUT_SCALEDOWN;
    
    // saturation logic on overload (not needed if the callback handles it)
    if (output >= 32767)
        output = 32767;
    else if (output <= -32768)
        output = -32768;
    
    // master output
    return (int)output;
}


unsigned int combinedWF(char num, char channel, unsigned int* wfarray, int index, char differ6581)
{
    if (differ6581 && SID_model[num] == 6581)
        index &= 0x7FF;
    
    return wfarray[index];
}


void createCombinedWF(unsigned int* wfarray, float bitmul, float bitstrength, float treshold)
{
    int i, j, k;
    
    for (i = 0; i < 4096; i++)
    {
        wfarray[i] = 0;
        
        for (j = 0; j < 12; j++)
        {
            float bitlevel = 0;
            
            for (k = 0; k < 12; k++)
                bitlevel += (bitmul / pow(bitstrength, fabs(k - j))) * (((i >> k) & 1) - 0.5);
            
            wfarray[i] += (bitlevel >= treshold) ? pow(2, j) : 0;
        }
        
        wfarray[i] *= 12;
    }
}

[See repo JSON]