//************************************************************** // ATMEL Studio 6 Inline Assembler-Routine // Soudausgabe auf DACA Chanal 0 // TimeR1 Interruptroutine: alle 25usec = 40.0 KHz Samplerate // // (Laufzeit: ?,??? µsec = ???7 Takte bei 32MHz) // Überarbeitete und erweiterte Version von Wiesolator // Stand: 15.07.2012 12:15 Uhr //************************************************************** #include "avr/io.h" .extern sound_out // Name der Assembler-Funktion .global TCC1_OVF_vect // Timer1 Interrupt-Vektor //--------------------------------------------------------------- // Benutzte Prozessor-Register (Definition als Namen) // habe hier alles umgeschrieben und neu platziert //--------------------------------------------------------------- phakku0 = 18 ; R18 Phasen-Akku Byte 0 phakku1 = 19 ; R19 Byte 1 phakku2 = 20 ; R20 Byte 2 delta0 = 21 ; R21 Phasen-Delta Byte 0 delta1 = 22 ; R22 Byte 1 delta2 = 23 ; R23 Byte 2 dcoout0 = 16 ; R16 DCO-Out Byte 0 dcoout1 = 17 ; R17 Byte 1 dcomix0 = 24 ; R24 DCO-Summe Byte 0 dcomix1 = 25 ; R25 Byte 1 dcaenv0 = 16 ; R20 DCA-Envelope Byte 0 dcaenv1 = 17 ; R21 Byte 1 dcaout0 = 20 ; R22 Multipl-Erg Byte 0 dcaout1 = 21 ; R23 Byte 1 dcaout2 = 22 ; R24 Byte 2 dcowave = 16 ; R16 DCO-Waveform 0:00 = Sine / Sine ; 1:01 = Sine / Saw ; 2:10 = Saw / Sine ; 3:11 = Saw / Saw //--------------------------------------------------------------- // Prozessor-Register inkl. Status-Register sichern // Interrupt-Routine Timer1-Overflow (40.000Hz) // ( 31 Takte = 0,969 µsec ) //--------------------------------------------------------------- TCC1_OVF_vect: PUSH R0 ; 2 R0 auf Stack schieben IN R0, SREG ; 1 Status-Register über bereits gesichertes PUSH R0 ; 2 R0 auf Stack schieben PUSH R1 ; 2 R1 auf Stack schieben PUSH R16 ; 2 R16 auf Stack schieben PUSH R17 ; 2 R17 auf Stack schieben PUSH R18 ; 2 R18 auf Stack schieben PUSH R19 ; 2 R19 auf Stack schieben PUSH R20 ; 2 R20 auf Stack schieben PUSH R21 ; 2 R21 auf Stack schieben PUSH R22 ; 2 R22 auf Stack schieben PUSH R23 ; 2 R23 auf Stack schieben PUSH R24 ; 2 R24 auf Stack schieben PUSH R25 ; 2 R25 auf Stack schieben PUSH R30 ; 2 R30 auf Stack schieben (ZL) PUSH R31 ; 2 R31 auf Stack schieben (ZH) //--------------------------------------------------------------- // DCO-1 // * 24-Bit Akku-Breite // * 24-Bit Phasen-Delta (2,384185mHz/Unit) // * 12-Bit Sample // ( 38 Takte = 1,188 µsec) //--------------------------------------------------------------- // Phasen-Akku 1 incrementieren // ---------------------------- DCO1Calc: LDS delta0, schrittweite1+0 ; 2 Phasen-Delta aus SRAM laden LDS delta1, schrittweite1+1 ; 2 LDS delta2, schrittweite1+2 ; 2 LDS phakku0, phaccu1+0 ; 2 Phasen-Akku aus SRAM laden LDS phakku1, phaccu1+1 ; 2 LDS phakku2, phaccu1+2 ; 2 SUB phakku0, delta0 ; 1 Phasen-Akku + Phasen-Delta SBC phakku1, delta1 ; 1 SBC phakku2, delta2 ; 1 STS phaccu1+0, phakku0 ; 2 Phasen-Akku in SRAM zurückschreiben STS phaccu1+1, phakku1 ; 2 STS phaccu1+2, phakku2 ; 2 // Die oberen 12Bit des Phasen-Akkus extrahieren // --------------------------------------------- ANDI phakku1, 0xF0 ; 1 Lower Nibble in Byte 0 abnullen LSR phakku2 ; 1 Division durch 8 (16-Bit) ROR phakku1 ; 1 LSR phakku2 ; 1 ROR phakku1 ; 1 LSR phakku2 ; 1 ROR phakku1 ; 1 // Waveform-Selektion // ------------------ LDS dcowave, DcoWaveForm ; 2 Wellenform-Selektion laden SBRS dcowave, 0 ; 1/2 RJMP DCO1Sine ; 2 Sinus bestimmen // Saw über 1:1 Phase ausgeben // --------------------------- DCO1Saw: MOV dcomix0, phakku1 ; 1 Phase umladen MOV dcomix1, phakku2 ; 1 RJMP DCO1End ; 2 Fertig // Sample über aktive Phase aus Wavetable laden // -------------------------------------------- DCO1Sine: LDI R30, 0xFC ; 1 Basis-Adresse Sinus-Tabelle (Low-Byte) LDI R31, 0x03 ; 1 (High-Byte) ADD R30, phakku1 ; 1 Phasen-Pointer addieren ADC R31, phakku2 ; 1 LPM dcomix0, Z+ ; 3 Sample aus Wavetable laden (16-Bit) LPM dcomix1, Z ; 3 => in MixerSumme als Initialwert DCO1End: //--------------------------------------------------------------- // DCO-2 // * 24-Bit Akku-Breite // * 24-Bit Phasen-Delta (2,384185mHz/Unit) // * 12-Bit Sample // ( 42 Takte = 1,313 µsec) //--------------------------------------------------------------- // Phasen-Akku 2 incrementieren // ---------------------------- DCO2Calc: LDS delta0, schrittweite2+0 ; 2 Phasen-Delta aus SRAM laden LDS delta1, schrittweite2+1 ; 2 LDS delta2, schrittweite2+2 ; 2 LDS phakku0, phaccu2+0 ; 2 Phasen-Akku aus SRAM laden LDS phakku1, phaccu2+1 ; 2 LDS phakku2, phaccu2+2 ; 2 SUB phakku0, delta0 ; 1 Phasen-Akku + Phasen-Delta SBC phakku1, delta1 ; 1 SBC phakku2, delta2 ; 1 STS phaccu2+0, phakku0 ; 2 Phasen-Akku in SRAM zurückschreiben STS phaccu2+1, phakku1 ; 2 STS phaccu2+2, phakku2 ; 2 // Die oberen 12Bit des Phasen-Akkus extrahieren // --------------------------------------------- ANDI phakku1, 0xF0 ; 1 Lower Nibble in Byte 0 abnullen LSR phakku2 ; 1 Division durch 8 (16-Bit) ROR phakku1 ; 1 LSR phakku2 ; 1 ROR phakku1 ; 1 LSR phakku2 ; 1 ROR phakku1 ; 1 // Waveform-Selektion // ------------------ LDS dcowave, DcoWaveForm ; 2 Wellenform-Selektion laden SBRS dcowave, 1 ; 1/2 RJMP DCO2Sine ; 2 Sinus bestimmen // Saw über 1:1 Phase ausgeben // --------------------------- DCO2Saw: // Mixer-Summe = (DCO1 + DCO2) / 2 // ------------------------------- ADD dcomix0, phakku1 ; 1 MixerSumme = DCO1 + DCO2 ADC dcomix1, phakku2 ; 1 RJMP DCO2End ; 2 Fertig // Sample über aktive Phase aus Wavetable laden // -------------------------------------------- DCO2Sine: LDI R30, 0xFC ; 1 Basis-Adresse Sinus-Tabelle (Low-Byte) LDI R31, 0x03 ; 1 (High-Byte) ADD R30, phakku1 ; 1 Phasen-Pointer addieren ADC R31, phakku2 ; 1 LPM dcoout0, Z+ ; 3 Sample aus Wavetable laden (16-Bit) LPM dcoout1, Z ; 3 // Mixer-Summe = (DCO1 + DCO2) / 2 // ------------------------------- ADD dcomix0, dcoout0 ; 1 MixerSumme = DCO1 + DCO2 ADC dcomix1, dcoout1 ; 1 DCO2End: LSR dcomix1 ; 1 MixerSumme = MixerSumme / 2 ROR dcomix0 ; 1 // -------------------------------------------------------------- // Waveform-Manipulationen // -------------------------------------------------------------- // ************************************************************ // * Plathalter für richtige Stelle der Implementierung vor * // * dem DCF wegen der vollen Auflösung. Hier kommen mögliche * // * Wave-Shaper, Phaser oder Distortion zum Einsatz * // ************************************************************ // -------------------------------------------------------------- // DCF Digital-Controlled-Filter // -------------------------------------------------------------- // ************************************************************ // * Plathalter für richtige Stelle der Implementierung vor * // * dem DCA wegen der vollen Auflösung in jeder DCA-Phase. * // * alternativ externer Analog-Filter (VCF) über zweiten DCA * // ************************************************************ //--------------------------------------------------------------- // DCA Digital-Controlled-Amplifier (ADSR-Hüllkurve & Tremolo) // Das Tremolo sollte multiplikativ in der Main auf die ADSR1 // aufmoduliert werden, um keine Probleme mit Überläufen bzw. // Skalierungen zu bekommen. Weiterhin sollte die ADSR eine // e-Funktion annähern, um eine C-Lade/Entladekurve zu simulieren // DCAout = DCOmix * DCAenv // -------------------------------------------------------------- // 16x16=24-Bit Multiplikation // ( 28 Takte = 0,875 µsec ) //--------------------------------------------------------------- // Multiplikation: Hoffe das stimmt, habe es nicht genauer // kontrolliert, nur umformuliert! clr dcaout2 LDS dcaenv0, adsr_1+0 ; 2 Hüllkurvenwert "adsr_1" laden LDS dcaenv1, adsr_1+1 ; 2 MUL dcomix0, dcaenv0 ; 2 16Bit Multiplikation "sample1 * adsr_1" MOVW dcaout0, R0 ; 1 MUL dcomix0, dcaenv1 ; 2 ADD dcaout1, R0 ; 1 ADC dcaout2, R1 ; 1 MUL dcomix1, dcaenv0 ; 2 ADD dcaout1, R0 ; 1 ADC dcaout2, R1 ; 1 MUL dcomix1, dcaenv1 ; 2 ADD dcaout2, R0 ; 1 // 24Bit Ergebnisregister auf 12Bit für Ausgabe für DAC reduzieren LSR dcaout2 ; 1 /16 (/4096) ROR dcaout1 ; 1 LSR dcaout2 ; 1 ROR dcaout1 ; 1 LSR dcaout2 ; 1 ROR dcaout1 ; 1 LSR dcaout2 ; 1 ROR dcaout1 ; 1 // !!! Hier wird Byte1 und Byte2 verwendet um Clocks zu sparen !!! // !!! Byte0 wird ignoriert und unten als Lade-Register verwendet !!! DCAEnd: // -------------------------------------------------------------- // Symmetrierung der Waveform (wegen deinem Knack-Geräusch) // OUT = (IN * DCA) + 0,5 - (DCA / 2) // OUT = (IN * DCAENV / 256 / 16) + 2048 - (DCAENV / 2) // ( 6 Takte = 0,188 µsec ) // -------------------------------------------------------------- LDI dcaout0, 0x08 ; 1 DCAOut = DCAOut + 2048 ADD dcaout2, dcaout0 ; 1 LSR dcaenv1 ; 1 DCAenv = DCAenv / 2 ROR dcaenv0 ; 1 SUB dcaout1, dcaenv0 ; 1 DCAOut = DCAOut - DCAenv SBC dcaout2, dcaenv1 ; 1 // -------------------------------------------------------------- // Ausgabe am DAC-Converter // -------------------------------------------------------------- // 16-Bit-Wert mit 0 bis 4095 auf DAC legen STS 0x0318, dcaout1 ; 2 L-Byte to DAC-Register (CH0DATAL Adr. 0x0318) STS 0x0319, dcaout2 ; 2 H-Byte to DAC Register (CH0DATAH Adr. 0x0319) // -------------------------------------------------------------- // Prozessor-Register inkl. Status-Register wiederherstellen // ( 35 Takte = 1,094 µsec ) // -------------------------------------------------------------- POP R31 ; 2 R31 von Stack wiederherstellen (ZH) POP R30 ; 2 R30 von Stack wiederherstellen (ZL) POP R25 ; 2 R25 von Stack wiederherstellen POP R24 ; 2 R24 von Stack wiederherstellen POP R23 ; 2 R23 von Stack wiederherstellen POP R22 ; 2 R22 von Stack wiederherstellen POP R21 ; 2 R21 von Stack wiederherstellen POP R20 ; 2 R20 von Stack wiederherstellen POP R19 ; 2 R19 von Stack wiederherstellen POP R18 ; 2 R18 von Stack wiederherstellen POP R17 ; 2 R17 von Stack wiederherstellen POP R16 ; 2 R16 von Stack wiederherstellen POP R1 ; 2 R1 von Stack wiederherstellen POP R0 ; 2 Status-Register über R0 wieder OUT SREG, R0 ; 1 herstellen POP R0 ; 2 R0 von Stack wiederherstellen RETI ; 4 Return Interrupt und I-Flag quittieren // -------------------------------------------------------------- .end //