| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /*
- SID.cpp - Atmega8 MOS6581 SID Emulator
- Copyright (c) 2007 Christoph Haberer, christoph(at)roboterclub-freiburg.de
- Arduino Library Conversion by Mario Patino, cybernesto(at)gmail.com
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
- /************************************************************************
- Atmega8 MOS6581 SID Emulator
- SID = Sound Interface Device
- This program tries to emulate the sound chip SID of the famous
- historical C64 Commodore computer.
- The SID emulator includes all registers of the original SID, but
- some functions may not be implemented yet.
- If you want to program the SID registers to generate your own sound,
- please refer to the MOS6581 datasheet. The emulator tries to be as
- compatible as possible.
- In the main program there is an interrupt routine which sets the
- ouptut values for the PWM-Output at 62.5kHz. Therefore the high
- frequency noise of the PWM should not be audible to normal people.
- The output is calculated with a 16kHz sample frequency to save
- processing cycles.
-
- The envelope generators are updated every 1ms.
- The amplitude value is output as an 8Bit PWM value.
- The PWM-Output may be directly connected to an audio amplifier.
-
- ************************************************************************
- Hardware
-
- processor: ATMEGA8, ATMEGA168
- clock: 16MHz Crystal
- PIN15 PB1/OC1A 8Bit PWM sound output
- PIN19 PB0 test LED
- ***************************************************************************/
- #include <avr/interrupt.h>
- #include "SID.h"
- // attack, decay, release envelope timings
- const static uint16_t AttackRate[16]={2,4,16,24,38,58,68,80,100,250,500,800,1000,3000,5000,8000};
- const static uint16_t DecayReleaseRate[16]={6,24,48,72,114,168,204,240,300,750,1500,2400,3000,9000,15000,24000};
-
- static uint8_t output;
- static Sid_t Sid;
- static Oscillator_t osc[OSCILLATORS];
- void initialize()
- {
- // TIMER1 used to generate sound output
- // TIMER1: Fast PWM 8-bit
- TCCR1A = (1 << WGM10) | (1 << COM1A1) ;
- // TIMER1: no prescaling
- TCCR1B = (1 << WGM12) | (1 << CS10);
-
- #if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__)
- // TIMER2 used to generate sample and ms interrupts
- // TIMER2: Normal Mode
- TCCR2 = 0;
- // TIMER2: clock/8 prescaling
- TCCR2 |= (1 << CS21);
- // TIMER2: set compare value to generate a 16kHz sample rate
- OCR2 = SAMPLERATECOUNT;
- // interrupt mask register: enable timer2 OCR2A interrupt
- TIMSK = (1 << OCIE2);
-
- // interrupt mask register: enable timer1 overflow
- TIMSK |= (1 << TOIE1);
- #else
- // TIMER2 used to generate sample and ms interrupts
- // TIMER2: Normal Mode
- TCCR2A = 0 ;
- // TIMER2: clock/8 prescaling
- TCCR2B = (1 << CS21);
- // TIMER2: set compare value to generate a 16kHz sample rate
- OCR2A = SAMPLERATECOUNT;
- // interrupt mask register: enable timer2 OCR2A interrupt
- TIMSK2 = (1 << OCIE2A);
-
- // interrupt mask register: enable timer1 overflow
- TIMSK1 = (1 << TOIE1);
- #endif
- }
- static int8_t wave(Voice_t *voice, uint16_t phase)
- {
- int8_t out;
- uint8_t n = phase >> 8;
- uint8_t wavetype = voice->ControlReg;
-
- if(wavetype & SAWTOOTH)
- {
- out = n - 128;
- }
- if(wavetype & TRIANGLE)
- {
- if(n&0x80)
- out = ((n^0xFF)<<1)-128;
- else
- out = (n<<1)-128;
- }
- if(wavetype & RECTANGLE)
- {
- if(n > (voice->PW >> 4)) // SID has 12Bit pwm, here we use only 8Bit
- out = 127;
- else
- out = -127;
- }
-
- return out;
- }
- static void waveforms()
- {
- static uint16_t phase[3], sig[3];
- static int16_t temp,temp1;
- static uint8_t i,j,k;
- static uint16_t noise = 0xACE1;
- static uint8_t noise8;
- static uint16_t tempphase;
- // noise generator based on Galois LFSR
- noise = (noise >> 1) ^ (-(noise & 1) & 0xB400u);
- noise8 = noise>>8;
-
- for(i = 0; i< 3; i++)
- {
- j = (i == 0 ? 2 : i - 1);
- tempphase=phase[i]+osc[i].freq_coefficient; //0.88us
- if(Sid.block.voice[i].ControlReg&NOISE)
- {
- if((tempphase^phase[i])&0x4000) sig[i]=noise8*osc[i].envelope;
- }
- else
- {
- if(Sid.block.voice[i].ControlReg&RINGMOD)
- {
- if(phase[j]&0x8000)
- sig[i]=osc[i].envelope*-wave(&Sid.block.voice[i],phase[i]);
- else
- sig[i]=osc[i].envelope*wave(&Sid.block.voice[i],phase[i]);
- }
- else
- {
- if(Sid.block.voice[i].ControlReg&SYNC)
- {
- if(tempphase < phase[j])
- phase[i] = 0;
- }
- else
- sig[i]=osc[i].envelope*wave(&Sid.block.voice[i],phase[i]); //2.07us
- }
- }
- phase[i]=tempphase;
- }
-
- // voice filter selection
- temp=0; // direct output variable
- temp1=0; // filter output variable
- if(Sid.block.RES_Filt&FILT1) temp1+=sig[0];
- else temp+=sig[0];
- if(Sid.block.RES_Filt&FILT2) temp1+=sig[1];
- else temp+=sig[1];
- if(Sid.block.RES_Filt&FILT3) temp1+=sig[2];
- else if(!(Sid.block.Mode_Vol&VOICE3OFF))temp+=sig[2]; // voice 3 with special turn off bit
- //filterOutput = IIR2((struct IIR_filter*)&filter04_06, filterInput);
- //IIR2(filter04_06, temp1);
- k=(temp>>8)+128;
- k+=temp1>>10; // no real filter implemeted yet
-
- output = k; // Output to PWM
- }
- static void envelopes()
- {
- uint8_t n;
- uint8_t controll_regadr[3]={4,11,18};
- // if gate is ONE then the attack,decay,sustain cycle begins
- // if gate switches to zero the sound decays
- for(n=0;n<OSCILLATORS;n++)
- {
- if(Sid.sidregister[controll_regadr[n]]&GATE) // if gate set then attack,decay,sustain
- {
- if(osc[n].attackdecay_flag)
- { // if attack cycle
- osc[n].amp+=osc[n].m_attack;
- if(osc[n].amp>MAXLEVEL)
- {
- osc[n].amp=MAXLEVEL;
- osc[n].attackdecay_flag=false; // if level reached, then switch to decay
- }
- }
- else // decay cycle
- {
- if(osc[n].amp>osc[n].level_sustain)
- {
- osc[n].amp-=osc[n].m_decay;
- if(osc[n].amp<osc[n].level_sustain) osc[n].amp=osc[n].level_sustain;
- }
- }
- }
- else // if gate flag is not set then release
- {
- osc[n].attackdecay_flag=true; // at next attack/decay cycle start wiht attack
- if(osc[n].amp>0)
- {
- osc[n].amp-=osc[n].m_release;
- if(osc[n].amp<0) osc[n].amp=0;
- }
- }
- osc[n].envelope=osc[n].amp>>8;
- }
- }
- /************************************************************************
- interrupt routine timer 1 overflow
- - set PWM output
-
- ************************************************************************/
- ISR(TIMER1_OVF_vect)
- {
- OCR1A = output; // Output to PWM
- }
- /************************************************************************
- interrupt routine timer 2 16kHz
- - calculate waverform phases
- - calculate waveforms
- - calculate attack decay release (1kHz)
-
- ************************************************************************/
- #if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__)
- ISR(TIMER2_COMP_vect)
- {
- static uint8_t mscounter = 0;
- OCR2 += SAMPLERATECOUNT; // Output to PWM
- waveforms(); //~22us
-
- if(mscounter++ >= MSCOUNT)
- {
- envelopes(); //~16us
- mscounter = 0;
- }
- }
- #else
- ISR(TIMER2_COMPA_vect)
- {
- static uint8_t mscounter = 0;
- OCR2A += SAMPLERATECOUNT; // Output to PWM
- waveforms(); //~36us
-
- if(mscounter++ >= MSCOUNT)
- {
- envelopes(); //~16us
- mscounter = 0;
- }
- }
- #endif
- // Constructor /////////////////////////////////////////////////////////////////
- // Function that handles the creation and setup of instances
- void SID::begin()
- {
- pinMode(9, OUTPUT);
- initialize();
-
- //initialize SID-registers
- Sid.sidregister[6]=0xF0;
- Sid.sidregister[13]=0xF0;
- Sid.sidregister[20]=0xF0;
-
- // set all amplitudes to zero
- for(int n=0;n<OSCILLATORS;n++) {
- osc[n].attackdecay_flag=true;
- setenvelope(&Sid.block.voice[n]);
- osc[n].amp=0;
- }
- }
- // Public Methods //////////////////////////////////////////////////////////////
- // Functions available in Wiring sketches, this library, and other libraries
- /************************************************************************
-
- uint8_t set_sidregister(uint8_t regnum, uint8_t value)
- The registers of the virtual SID are set by this routine.
- For some registers it is necessary to transform the SID-register
- values to some internal settings of the emulator.
- To select this registers and to start the calculation, the switch/
- case statement is used.
- For instance: If setting the SID envelope register, new attach, decay
- sustain times are calculated.
- If an invalid register is requested the returned value will be 0.
- 4.2007 ch
- ************************************************************************/
- uint8_t SID::set_register(uint8_t regnum, uint8_t value)
- {
- if(regnum>NUMREGISTERS-1)
- return 0;
-
- Sid.sidregister[regnum]=value;
- switch(regnum)
- {
- //voice1
- case 1:
- osc[0].freq_coefficient=((uint16_t)Sid.sidregister[0]+((uint16_t)Sid.sidregister[1]<<8))>>2;
- break;
- case 5: setenvelope(&Sid.block.voice[0]);break;
- case 6: setenvelope(&Sid.block.voice[0]);break;
-
- //voice2
- case 8:
- osc[1].freq_coefficient=((uint16_t)Sid.sidregister[7]+((uint16_t)Sid.sidregister[8]<<8))>>2;
- break;
- case 12: setenvelope(&Sid.block.voice[1]);break;
- case 13: setenvelope(&Sid.block.voice[1]);break;
-
- //voice3
- case 15:
- osc[2].freq_coefficient=((uint16_t)Sid.sidregister[14]+((uint16_t)Sid.sidregister[15]<<8))>>2;
- break;
- case 19: setenvelope(&Sid.block.voice[2]);break;
- case 20: setenvelope(&Sid.block.voice[2]);break;
- }
- return 1;
- }
- /************************************************************************
-
- uint8_t get_sidregister(uint8_t regnum)
- The registers of the virtual SID are read by this routine.
- If an invalid register is requested it returns zero.
- ************************************************************************/
- uint8_t SID::get_register(uint8_t regnum)
- {
- if(regnum>NUMREGISTERS-1)
- return 0;
- return Sid.sidregister[regnum];
- }
- // Private Methods /////////////////////////////////////////////////////////////
- // Functions only available to other functions in this library
- uint8_t SID::get_wavenum(Voice_t *voice)
- {
- uint8_t n;
- if(voice==&Sid.block.voice[0]) n=0;
- if(voice==&Sid.block.voice[1]) n=1;
- if(voice==&Sid.block.voice[2]) n=2;
-
- return n;
- }
- void SID::setfreq(Voice_t *voice,uint16_t freq)
- {
- uint32_t templong;
- uint8_t n;
- n=get_wavenum(voice);
-
- templong=freq;
- osc[n].freq_coefficient=templong*4000/SAMPLEFREQ;
- }
- void SID::setenvelope(Voice_t *voice)
- {
- uint8_t n;
-
- n=get_wavenum(voice);
- osc[n].attackdecay_flag=true;
- osc[n].level_sustain=(voice->SustainRelease>>4)*SUSTAINFACTOR;
- osc[n].m_attack=MAXLEVEL/AttackRate[voice->AttackDecay>>4];
- osc[n].m_decay=(MAXLEVEL-osc[n].level_sustain*SUSTAINFACTOR)/DecayReleaseRate[voice->AttackDecay&0x0F];
- osc[n].m_release=(osc[n].level_sustain)/DecayReleaseRate[voice->SustainRelease&0x0F];
- }
|