;------------------------------------------------------------------------------------------------------------------- ; AVRSYN.ASM 020227/050505 ; Synthesiser on the ATmega16 ; ; PA7..0 A/D ; PB0-PB4 Control Panel Switches - ROWS ; PB5-PB7 In Circuit Programming ; PC0-PC7 DATA port for 2 x HC574 ; PD0 RXD (MIDI IN) ; PD1 LED ; PD2 WRL\ ; PD3 WRH\ ; PD4-PD7 Control Panel Switches - COLUMNS ; ; Timer0 not used ; Timer1 Time counting: (CK/256) --> TCNT1 (32 uS) ; Timer2 Sampling timer: (CK/8)/32 --> 31.25kHz ; ; OSCILLATOR: ; I used the "direct frequency synthesis". A three-byte counter (= phase) is ; being incremented by a value which is proportional to the sound frequency ; (= phase delta). The increment takes place every sampling period, i.e. each ; 32 us. The most significant byte of the counter is the sawtooth wave. ; The square wave is a result of comparing the sawtooth wave to 128. ; Each oscillator has its own "phase" and "phase delta" registers. ; The contents of each "phase delta" register depends on the frequency ; being generated: ; ; PHASE DELTA = 2^24 * Freq / SamplingFreq ; ; where: ; SamplingFreq = 31250Hz ; Freq = 440Hz * 2^((n-69+d)/12) ; where in turn: ; n = MIDI note number ; d = transpose/detune (in halftones) ; ; FILTER: ; This is probably the simplest low pass digital filter ever invented ; the formula is: ; ; y[i] = y[i-1] + m*( x[i] - y[i-1] ) ; ; The greater the m, the higher the cutoff frequency. For m=0 there is no ; AC signal on the output, and for m=1 the output is a copy of the input. ; I used 16 bit shifts in order to multiply by m because the AT90S8535 ; has no multiplier. ;------------------------------------------------------------------------------------------------------------------- .NOLIST .INCLUDE "m16def.inc" .LIST .LISTMAC .SET KBDSCANPERIOD = 3125 ;Key scan period: 3125 * 32µs = 100ms 050419 ;------------------------------------------------------------------------------------------------------------------- ; V A R I A B L E S ;------------------------------------------------------------------------------------------------------------------- ;registry: ; R0 reserved for the "lpm" instruction .DEF ZERO = R1 ;current phase of DCO A: .DEF PHASEA_0 = R2 .DEF PHASEA_1 = R3 .DEF PHASEA_2 = R4 ;current phase of DCO B: .DEF PHASEB_0 = R5 .DEF PHASEB_1 = R6 .DEF PHASEB_2 = R7 ;increase phase for DCO A: .DEF DELTAA_0 = R8 .DEF DELTAA_1 = R9 .DEF DELTAA_2 = R10 ;increase phase for DCO B: .DEF DELTAB_0 = R11 .DEF DELTAB_1 = R12 .DEF DELTAB_2 = R13 ;DCF: .DEF YN1_L = R14 .DEF YN1_H = R15 ;RAM (0060h...025Fh): .DSEG ;MIDI: MIDIPHASE: .BYTE 1 MIDICHANNEL: .BYTE 1 MIDIDATA0: .BYTE 1 MIDIVELOCITY: .BYTE 1 MIDINOTE: .BYTE 1 MIDINOTEPREV: .BYTE 1 ; buffer for MIDI note MIDIPBEND_L: .BYTE 1 ;\ MIDIPBEND_H: .BYTE 1 ;/ -32768..+32766 MIDIMODWHEEL: .BYTE 1 ;0..254 ;current sound parameters: LFOFREQ: .BYTE 1 ;0..255 LFOLEVEL: .BYTE 1 ;0..255 MODEFLAGS1: .BYTE 1 ;b0 = DCO DIST: 0=off, 1=on ;b1 = wave A: 0=saw, 1=squ ;b2 = wave B: 0=saw, 1=squ ;b3 = osc B: 0=off, 1=on ;b4 = DCA mode: 0=gate, 1=env ;b5 = transpose: 0=down, 1=up ;b6 = octave A: 0=down, 1=up ;b7 = octave B: 0=down, 1=up MODEFLAGS2: .BYTE 1 ;b0 = LFO MODE: 0=DCO, 1=DCF ;b1 = LFO WAVE: 0=tri, 1=squ ;b2 = DCF KBD TRACK: 0=off, 1=on ;b3 = ENV LOOP: 0=off, 1=on ;b4 = ENV MODE: 0 = A-R, 1 = A-S-R ;b5 = ;b6 = LFO KBD SYNC: 0=off, 1=on ;b7 = LFO: 0=norm, 1=rand SETMIDICHANNEL: .BYTE 1 ;selected MIDI channel: 0 for OMNI or 1..15 DETUNEB_FRAC: .BYTE 1 ;\ DETUNEB_INTG: .BYTE 1 ;/ -128,000..+127,996 CUTOFF: .BYTE 1 ;0..255 VCFENVMOD: .BYTE 1 ;0..255 PORTAMENTO: .BYTE 1 ;0..255 ATTACKTIME: .BYTE 1 ;0..255 RELEASETIME: .BYTE 1 ;0..255 ;other variables: SWITCH1: .BYTE 1 ;bit meanings for switch-bank 1: ;b0: P.BEND disable/enable ;b1: MOD.WHEEL disable/enable ;b2: LFO KBD SYNC off/on ;b3: LFO norm/rand ;b4: ENV LOOP off/on ;b5: transpose down/up ;b6: octave A down/up ;b7: octave B down/up SWITCH2: .BYTE 1 ;bit meanings for switch-bank 2: ;b0: wave A saw/squ ;b1: wave B saw/squ ;b2: osc B off/on ;b3: DCA gate/env ;b4: env AR/ASR ;b5: LFO DCO/DCF ;b6: LFO tri/squ ;b7: DCF KBD TRACK SWITCH3: .BYTE 1 ;b0: MIDI SWITCH 1 050505 ;b1: MIDI SWITCH 2 ;b2: MIDI SWITCH 3 ;b3: MIDI SWITCH 4 NOTE_L: .BYTE 1 NOTE_H: .BYTE 1 NOTE_INTG: .BYTE 1 PORTACNT: .BYTE 1 ;2 / 1 / 0 LPF_I: .BYTE 1 LEVEL: .BYTE 1 ;0..255 PITCH: .BYTE 1 ;0..96 ADC_CHAN: .BYTE 1 ;0..7 ADC_0: .BYTE 1 ADC_1: .BYTE 1 ADC_2: .BYTE 1 ADC_3: .BYTE 1 ADC_4: .BYTE 1 ADC_5: .BYTE 1 ADC_6: .BYTE 1 ADC_7: .BYTE 1 GATE: .BYTE 1 ;0 / 1 GATEEDGE: .BYTE 1 ;0 / 1 TPREV_KBD_L: .BYTE 1 TPREV_KBD_H: .BYTE 1 TPREV_L: .BYTE 1 TPREV_H: .BYTE 1 DELTAT_L: .BYTE 1 ;\ Time from former course DELTAT_H: .BYTE 1 ;/ of the main loop (1 bit = 32 µs) ENVPHASE: .BYTE 1 ;0=stop 1=attack 2=sustain 3=release ENV_FRAC_L: .BYTE 1 ENV_FRAC_H: .BYTE 1 ENV_INTEGR: .BYTE 1 LFOPHASE: .BYTE 1 ;0=up 1=down LFO_FRAC_L: .BYTE 1 ;\ LFO_FRAC_H: .BYTE 1 ; > -128,000..+127,999 LFO_INTEGR: .BYTE 1 ;/ LFOVALUE: .BYTE 1 ;-128..+127 ; 0x97 OLDWAVEA: .BYTE 1 OLDWAVEB: .BYTE 1 SHIFTREG_0: .BYTE 1 ;\ SHIFTREG_1: .BYTE 1 ; > shift register for SHIFTREG_2: .BYTE 1 ;/ pseudo-random generator LFOBOTTOM_0: .BYTE 1 ;\ LFOBOTTOM_1: .BYTE 1 ; > bottom level of LFO LFOBOTTOM_2: .BYTE 1 ;/ LFOTOP_0: .BYTE 1 ;\ LFOTOP_1: .BYTE 1 ; > top level of LFO LFOTOP_2: .BYTE 1 ;/ ;stack: 0x0A3..0x25F ;------------------------------------------------------------------------------------------------------------------- ; V E C T O R T A B L E ;------------------------------------------------------------------------------------------------------------------- .CSEG .ORG 0x0000 jmp RESET ;0x000 RESET jmp IRQ_NONE ;0x001 INT0 jmp IRQ_NONE ;0x002 INT1 jmp TIM2_CMP ;0x003 TIMER2 COMP jmp IRQ_NONE ;0x004 TIMER2 OVF jmp IRQ_NONE ;0x005 TIMER1 CAPT jmp IRQ_NONE ;0x006 TIMER1 COMPA jmp IRQ_NONE ;0x007 TIMER1 COMPB jmp IRQ_NONE ;0x008 TIMER1 OVF jmp IRQ_NONE ;0x009 TIMER0 OVF jmp IRQ_NONE ;0x00A SPI,STC jmp UART_RXC ;0x00B UART,RX jmp IRQ_NONE ;0x00C UART,UDRE jmp IRQ_NONE ;0x00D UART,TX jmp IRQ_NONE ;0x00E ADC jmp IRQ_NONE ;0x00F EE_RDY jmp IRQ_NONE ;0x010 ANA_COMP IRQ_NONE: reti ;------------------------------------------------------------------------------------------------------------------- ; R O M T A B L E S ;------------------------------------------------------------------------------------------------------------------- ;----------------------------------------------------------------------------- DELTA_C: .DW 0x2559 ;\ .DW 0x0011 ;/ note 0 ( 8,175799 Hz) DELTA_CIS: .DW 0x2A5A ;\ .DW 0x0012 ;/ note 1 ( 8,661957 Hz) DELTA_D: .DW 0x3EE1 ;\ .DW 0x0013 ;/ note 2 ( 9,177024 Hz) DELTA_DIS: .DW 0x63D8 ;\ .DW 0x0014 ;/ note 3 ( 9,722718 Hz) DELTA_E: .DW 0x9A3C ;\ .DW 0x0015 ;/ note 4 (10,300861 Hz) DELTA_F: .DW 0xE314 ;\ .DW 0x0016 ;/ note 5 (10,913382 Hz) DELTA_FIS: .DW 0x3F7A ;\ .DW 0x0018 ;/ note 6 (11,562326 Hz) DELTA_G: .DW 0xB098 ;\ .DW 0x0019 ;/ note 7 (12,249857 Hz) DELTA_GIS: .DW 0x37A8 ;\ .DW 0x001B ;/ note 8 (12,978272 Hz) DELTA_A: .DW 0xD5FA ;\ .DW 0x001C ;/ note 9 (13,750000 Hz) DELTA_AIS: .DW 0x8CEE ;\ .DW 0x001E ;/ note 10 (14,567618 Hz) DELTA_H: .DW 0x5DFD ;\ .DW 0x0020 ;/ note 11 (15,433853 Hz) DELTA_C1: .DW 0x4AB2 ;\ .DW 0x0022 ;/ note 12 (16,351598 Hz) ;----------------------------------------------------------------------------- TAB_VCF: .DW 0x0101 .DW 0x0101 .DW 0x0201 .DW 0x0202 .DW 0x0202 .DW 0x0202 .DW 0x0202 .DW 0x0402 .DW 0x0404 .DW 0x0505 .DW 0x0606 .DW 0x0806 .DW 0x0908 .DW 0x0A0A .DW 0x0C0C .DW 0x100C .DW 0x1211 .DW 0x1614 .DW 0x1A18 .DW 0x201C .DW 0x2422 .DW 0x2C28 .DW 0x3430 .DW 0x4038 .DW 0x4C44 .DW 0x5852 .DW 0x6861 .DW 0x8070 .DW 0x968A .DW 0xB0A4 .DW 0xD8C4 .DW 0xFFE8 ;----------------------------------------------------------------------------- TAB_VCA: .DW 0x0100 ;-48.2dB,-oodB .DW 0x0302 ;-38.6dB,-42.1dB .DW 0x0504 ;-34.2dB,-36.1dB .DW 0x0706 ;-31.3dB,-32.6dB .DW 0x0908 ;-29.1dB,-30.1dB .DW 0x0C0A ;-26.6dB,-28.2dB .DW 0x110E ;-23.6dB,-25.2dB .DW 0x1814 ;-20.6dB,-22.1dB .DW 0x201C ;-18.1dB,-19.2dB .DW 0x2824 ;-16.1dB,-17.0dB .DW 0x302C ;-14.5dB,-15.3dB .DW 0x3834 ;-13.2dB,-13.8dB .DW 0x403C ;-12.0dB,-12.6dB .DW 0x4A46 ;-10.8dB,-11.3dB .DW 0x5450 ; -9.7dB,-10.1dB .DW 0x615A ; -8.4dB, -9.1dB .DW 0x7068 ; -7.2dB, -7.8dB .DW 0x8078 ; -6.0dB, -6.6dB .DW 0x9489 ; -4.8dB, -5.4dB .DW 0xA8A0 ; -3.7dB, -4.1dB .DW 0xC2B8 ; -2.4dB, -2.9dB .DW 0xE0D2 ; -1.2dB, -1.7dB .DW 0xFFF0 ; -0.0dB, -0.6dB ;----------------------------------------------------------------------------- ;rate = 536,870912 s / time[s] TIMETORATE: .DW 65535 ;8,192 ms .DW 50957 ;10,54 ms .DW 39621 ;13,55 ms .DW 30807 ;17,43 ms .DW 23953 ;22,41 ms .DW 18625 ;28,83 ms .DW 14481 ;37,07 ms .DW 11260 ;47,68 ms .DW 8755 ;61,32 ms .DW 6807 ;78,87 ms .DW 5293 ;101,4 ms .DW 4115 ;130,5 ms .DW 3200 ;167,8 ms .DW 2488 ;215,8 ms .DW 1935 ;277,5 ms .DW 1504 ;356,9 ms .DW 1170 ;459,0 ms .DW 909 ;590,4 ms .DW 707 ;759,3 ms .DW 550 ;976,5 ms .DW 427 ;1,256 s .DW 332 ;1,615 s .DW 258 ;2,077 s .DW 201 ;2,672 s .DW 156 ;3,436 s .DW 121 ;4,419 s .DW 94 ;5,684 s .DW 73 ;7,310 s .DW 57 ;9,401 s .DW 44 ;12,09 s .DW 35 ;15,55 s .DW 27 ;20,00 s ;------------------------------------------------------------------------------------------------------------------- ; I N T E R R U P T S U B R O U T I N E S ;------------------------------------------------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ; Timer 2 compare interrupt (sampling) ;----------------------------------------------------------------------------- TIM2_CMP: ;;sbi PORTB,0 ;PB0 = 1 (DIAG) push R16 in R16,SREG ;\ push R16 ;/ push SREG push R17 push R28 push R29 ;DCO A, DCO B: lds R28,MODEFLAGS1 ;DCO A: mov R17,PHASEA_2 rol R17 ;R17.7 --> Cy sbc R17,R17 ;R17 = 0 or 255 (square wave) sbrs R28,1 ;0/1 (DCO A = saw/squ) mov R17,PHASEA_2 ;only when sawtooth lds R29,OLDWAVEA sts OLDWAVEA,R17 add R17,R29 ;\ R17 = mean value of former ror R17 ;/ and present values ;DCO B: mov R16,PHASEB_2 rol R16 ;R16.7 --> Cy sbc R16,R16 ;R16 = 0 or 255 (square wave) sbrs R28,2 ;0/1 (DCO B = saw/squ) mov R16,PHASEB_2 ;only when sawtooth lds R29,OLDWAVEB sts OLDWAVEB,R16 add R16,R29 ;\ R16 = mean value of former ror R16 ;/ and present values sbrc R28,0 ;0/1 (OSC DIST = off/on) eor R17,R16 sbrs R28,3 ldi R16,128 ;when DCO B = off ;sum of DCO A and DCO B: add R17,R16 ;Cy:R17 = A + B ror R17 ;R17 = 0,5 * (A + B) lsr R17 ;R17 = 0,25 * (A + B) ldi R16,0 ;\ R17:R16 = A + B subi R17,64 ;/ -16384..+16383 ;DCF: lds R28,LPF_I cpi R28,255 breq T2_SKIPVCF sub R16,YN1_L ;\ sbc R17,YN1_H ;/ R17:R16 = x[n] - y[n-1] T2_FLOOP: asr R17 ;\ ror R16 ;/ R17:R16 = R17:R16 asr 1 lsl R28 ;R28.7 --> Carry brcc T2_FCHECK add YN1_L,R16 ;\ adc YN1_H,R17 ;/ y[n-1] + 1/k*(x[n]-y[n-1]) T2_FCHECK: tst R28 brne T2_FLOOP mov R16,YN1_L ;\ mov R17,YN1_H ;/ y[n-1] --> y[n] T2_SKIPVCF: ;DCA: ldi R28,0 ldi R29,0 push R31 lds R31,LEVEL cpi R31,255 brne T2_ACHECK ;multiply when LEVEL!=255 mov R28,R16 mov R29,R17 rjmp T2_AEXIT T2_ALOOP: asr R17 ;\ ror R16 ;/ R17:R16 = R17:R16 asr 1 lsl R31 ;Cy <-- R31 <-- 0 brcc T2_ACHECK add R28,R16 adc R29,R17 T2_ACHECK: tst R31 brne T2_ALOOP T2_AEXIT: pop R31 ;write sample (R29:R28) in DAC: subi R29,128 ;U2 --> PB cbi PORTD,2 ;WRL\ = 0 cbi PORTD,3 ;WRH\ = 0 out PORTC,R28 ;LSByte sbi PORTD,2 ;WRL\ = 1 out PORTC,R29 ;MSByte sbi PORTD,3 ;WRH\ = 1 ;increment phase: add PHASEA_0,DELTAA_0 adc PHASEA_1,DELTAA_1 adc PHASEA_2,DELTAA_2 add PHASEB_0,DELTAB_0 adc PHASEB_1,DELTAB_1 adc PHASEB_2,DELTAB_2 ;end: pop R29 pop R28 pop R17 pop R16 ;\ out SREG,R16 ;/ pop SREG pop R16 ;;cbi PORTB,0 ;PB0 = 0 (DIAG) reti ;----------------------------------------------------------------------------- ; UART receiver (MIDI IN) ;----------------------------------------------------------------------------- UART_RXC: push R16 in R16,SREG ;\ push R16 ;/ push SREG in R16,UDR ;read received byte in R16 cbi UCR,7 ;RXCIE=0 (disable UART interrupts) sei ;enable other interrupts push R17 tst R16 ;\ jump when brpl INTRX_DATA ;/ R16.7 == 0 (MIDI data byte) ;MIDI status byte (1xxxxxxx): mov R17,R16 andi R17,0xF0 cpi R17,0x80 breq INTRX_ACCEPT ;8x note off cpi R17,0x90 breq INTRX_ACCEPT ;9x note on cpi R17,0xB0 breq INTRX_ACCEPT ;Bx control change cpi R17,0xE0 breq INTRX_ACCEPT ;Ex pitch bend ldi R17,0 ;\ sts MIDIPHASE,R17 ;/ MIDIPHASE = 0 rjmp INTRX_EXIT ;Ax polyphonic aftertouch ;Cx program change ;Dx channel aftertouch ;Fx system INTRX_ACCEPT: sts MIDIPHASE,R17 ;phase = 80 90 B0 E0 andi R16,0x0F ;\ inc R16 ; > store MIDI channel 1..16 sts MIDICHANNEL,R16 ;/ lds R17,SETMIDICHANNEL ;0 for OMNI or 1..15 050505 tst R17 breq INTRX_ACPT_X ;end when OMNI cp R17,R16 ;compare set channel to the incoming channel breq INTRX_ACPT_X ;end when right channel ldi R17,0 ;\ otherwise: sts MIDIPHASE,R17 ;/ MIDIPHASE = 0 (no data service) INTRX_ACPT_X: rjmp INTRX_EXIT ;MIDI data byte (0xxxxxxx): INTRX_DATA: lds R17,MIDIPHASE cpi R17,0x80 ;\ breq INTRX_NOFF1 ; \ cpi R17,0x81 ; / note off breq INTRX_NOFF2 ;/ rjmp INTRX_NOTEON INTRX_NOFF1: inc R17 ;\ sts MIDIPHASE,R17 ;/ MIDIPHASE = 0x81 sts MIDIDATA0,R16 ;MIDIDATA0 = d rjmp INTRX_EXIT INTRX_NOFF2: dec R17 ;\ sts MIDIPHASE,R17 ;/ MIDIPHASE = 0x80 rjmp INTRXNON2_OFF ;9x note on: INTRX_NOTEON: cpi R17,0x90 ;\ breq INTRX_NON1 ; \ cpi R17,0x91 ; / note on breq INTRX_NON2 ;/ rjmp INTRX_CTRL INTRX_NON1: inc R17 ;\ sts MIDIPHASE,R17 ;/ MIDIPHASE = 0x91 sts MIDIDATA0,R16 ;MIDIDATA0 = d rjmp INTRX_EXIT INTRX_NON2: dec R17 ;\ sts MIDIPHASE,R17 ;/ MIDIPHASE = 0x90 tst R16 ;\ brne INTRXNON2_ON ;/ jump when velocity != 0 ;turn note off: INTRXNON2_OFF: lds R16,MIDIDATA0 lds R17,MIDINOTEPREV cp R16,R17 brne INTRXNON2_OFF1 ldi R17,255 ;\ remove previous note sts MIDINOTEPREV,R17 ;/ from buffer INTRXNON2_OFF1: lds R17,MIDINOTE cp R16,R17 ;\ brne INTRXNON2_OFF3 ;/ exit when not the same note lds R17,MIDINOTEPREV cpi R17,255 breq INTRXNON2_OFF2 sts MIDINOTE,R17 ;previous note is valid ldi R17,255 ;\ remove previous note sts MIDINOTEPREV,R17 ;/ from buffer INTRXNON2_OFF3: rjmp INTRX_EXIT INTRXNON2_OFF2: ldi R17,255 ;\ remove last note sts MIDINOTE,R17 ;/ ldi R17,0 ;\ sts GATE,R17 ;/ GATE = 0 cbi PORTD,1 ;LED off rjmp INTRX_EXIT ;turn note on: INTRXNON2_ON: sts MIDIVELOCITY,R16 ;store velocity lds R17,MIDINOTE ;\ move previous note sts MIDINOTEPREV,R17 ;/ into buffer lds R17,MIDIDATA0 ;\ sts MIDINOTE,R17 ;/ MIDINOTE = note# ldi R17,1 sts GATE,R17 ;GATE = 1 sts GATEEDGE,R17 ;GATEEDGE = 1 sbi PORTD,1 ;LED on rjmp INTRX_EXIT ;Bx control change: INTRX_CTRL: cpi R17,0xB0 ;\ breq INTRX_CC1 ; \ cpi R17,0xB1 ; / control change breq INTRX_CC2 ;/ rjmp INTRX_PBEND INTRX_CC1: inc R17 ;\ sts MIDIPHASE,R17 ;/ MIDIPHASE = 0xB1 sts MIDIDATA0,R16 ;MIDIDATA0 = controller# rjmp INTRX_EXIT INTRX_CC2: dec R17 ;\ sts MIDIPHASE,R17 ;/ MIDIPHASE = 0xB0 lds R17,MIDIDATA0 cpi R17,1 brne INTRX_EXIT ;skip if not MOD.WHEEL lsl R16 sts MIDIMODWHEEL,R16 rjmp INTRX_EXIT ;Ex pitch bender: INTRX_PBEND: cpi R17,0xE0 ;\ breq INTRX_PB1 ; \ cpi R17,0xE1 ; / pitch bend breq INTRX_PB2 ;/ rjmp INTRX_EXIT INTRX_PB1: inc R17 ;\ sts MIDIPHASE,R17 ;/ MIDIPHASE = 0xE1 sts MIDIDATA0,R16 ;MIDIDATA0 = dFine 0..127 rjmp INTRX_EXIT INTRX_PB2: dec R17 ;\ sts MIDIPHASE,R17 ;/ MIDIPHASE = 0xE0 lds R17,MIDIDATA0 ;\ lsl R17 ;/ R17 = dFine*2 0..254 lsl R17 ;\ R16,R17 = P.B.data rol R16 ;/ 0..255,996 subi R16,128 ;R16,R17 = -128,000..+127,996 sts MIDIPBEND_L,R17 ;\ sts MIDIPBEND_H,R16 ;/ store P.BEND value rjmp INTRX_EXIT INTRX_EXIT: pop R17 pop R16 ;\ out SREG,R16 ;/ pop SREG pop R16 sbi UCR,7 ;RXCIE=1 reti ;------------------------------------------------------------------------------------------------------------------- ; M A I N L E V E L S U B R O U T I N E S ;------------------------------------------------------------------------------------------------------------------- ;============================================================================= ; Delay subroutines ;============================================================================= WAIT_10US: push R16 ;3+2 ldi R16,50 ;1 W10U_LOOP: dec R16 ;1 \ brne W10U_LOOP ;2/1 / 49*3 + 2 pop R16 ;2 ret ;4 ;============================================================================= ; I/O subroutines ;============================================================================= ;----------------------------------------------------------------------------- ;A/D conversion (start) ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R18 = channel # 0..7 ;Out: - ;Used: - ;----------------------------------------------------------------------------- ADC_START: out ADMUX,R18 ;set multiplexer sbi ADCSR,6 ;ADSC=1 ret ;----------------------------------------------------------------------------- ;A/D conversion (end) ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: - ;Out: R16 = result 0..255 ;Used: SREG,R17 ;----------------------------------------------------------------------------- ADC_END: ADCE_LOOP: sbis ADCSR,4 ;\ rjmp ADCE_LOOP ;/ wait for ADIF==1 sbi ADCSR,4 ;clear ADIF 050416 in R16,ADCL ;\ in R17,ADCH ;/ R17:R16 = 000000Dd:dddddddd lsr R17 ;\ ror R16 ;/ R17:R16 = 0000000D:dddddddd lsr R17 ;\ ror R16 ;/ R16 = Dddddddd ret ;============================================================================= ; arithmetic subroutines ;============================================================================= ;----------------------------------------------------------------------------- ; 16 bit arithmetical shift right (division by 2^n) ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R17:R16 = x ; R18 = n (shift count) 0..16 ;Out: R17:R16 = x asr n ;Used: SREG ;----------------------------------------------------------------------------- ASR16: tst R18 breq ASR16_EXIT push R18 ASR16_LOOP: asr R17 ;\ ror R16 ;/ R17,R16 = R17,R16 asr 1 dec R18 brne ASR16_LOOP pop R18 ASR16_EXIT: ret ;----------------------------------------------------------------------------- ; 32 bit logical shift right ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R19:R18:R17:R16 = x ; R20 = n (shift count) ;Out: R19:R18:R17:R16 = x >> n ;Used: SREG ;----------------------------------------------------------------------------- SHR32: tst R20 breq SHR32_EXIT push R20 SHR32_LOOP: lsr R19 ror R18 ror R17 ror R16 dec R20 brne SHR32_LOOP pop R20 SHR32_EXIT: ret ;----------------------------------------------------------------------------- ; 32 bit logical shift left ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R19:R18:R17:R16 = x ; R20 = n (shift count) ;Out: R19:R18:R17:R16 = x << n ;Used: SREG ;----------------------------------------------------------------------------- SHL32: tst R20 breq SHL32_EXIT push R20 SHL32_LOOP: lsl R16 rol R17 rol R18 rol R19 dec R20 brne SHL32_LOOP pop R20 SHL32_EXIT: ret ;----------------------------------------------------------------------------- ;8 bit x 8 bit multiplication (unsigned) ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R16 = x 0..255 ; R17 = y 0,000..0,996 ;Out: R17,R16 = x * y 0,000..254,004 ;Used: SREG,R18-R20 ;----------------------------------------------------------------------------- MUL8X8U: ldi R18,0 ;\ mov R19,R16 ;/ R19,R18 = x,0 mov R20,R17 clr R16 clr R17 rjmp M88_CHECK M88_LOOP: lsr R19 ;\ ror R18 ;/ R19,R18 = R19,R18 shr 1 lsl R20 brcc M88_CHECK add R16,R18 adc R17,R19 M88_CHECK: tst R20 brne M88_LOOP ret ;----------------------------------------------------------------------------- ;8 bit x 8 bit multiplication (signed) ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R16 = x -128..+127 ; R17 = y 0,000..0,996 ;Out: R17,R16 = x * y -127,500..+126,504 ;Used: SREG,R18-R20 ;----------------------------------------------------------------------------- MUL8X8S: bst R16,7 ;T = sign: 0=plus, 1=minus sbrc R16,7 ;\ neg R16 ;/ R16 = abs(R16) 0..128 rcall MUL8X8U ;R17,R16 = LFO * LFOMOD brtc M8X8S_EXIT ;exit if x >= 0 com R16 ;\ com R17 ; \ sec ; > R17:R16 = -R17:R16 adc R16,ZERO ; / adc R17,ZERO ;/ M8X8S_EXIT: ret ;----------------------------------------------------------------------------- ;32 bit x 16 bit multiplication (unsigned) ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R19:R18:R17:R16 = x 0..2^32-1 ; R23:R22 = y 0,yyyyyyyyyyyyyyyy 0..0,9999847 ;Out: R19:R18:R17:R16 = x * y 0..2^32-1 ;Used: SREG,R20-R29 ;----------------------------------------------------------------------------- MUL32X16: push R30 clr R20 ;\ clr R21 ;/ XX = x clr R24 ;\ clr R25 ; \ clr R26 ; \ clr R27 ; / ZZ = 0 clr R28 ; / clr R29 ;/ rjmp M3216_CHECK M3216_LOOP: lsr R23 ;\ ror R22 ;/ y:Carry = y >> 1 brcc M3216_SKIP add R24,R16 ;\ adc R25,R17 ; \ adc R26,R18 ; \ adc R27,R19 ; / ZZ = ZZ + XX adc R28,R20 ; / adc R29,R21 ;/ M3216_SKIP: lsl R16 ;\ rol R17 ; \ rol R18 ; \ rol R19 ; / YY = YY << 1 rol R20 ; / rol R21 ;/ M3216_CHECK: mov R30,R22 ;\ or R30,R23 ;/ check if y == 0 brne M3216_LOOP mov R16,R26 ;\ mov R17,R27 ; \ mov R18,R28 ; / x * y mov R19,R29 ;/ pop R30 ret ;----------------------------------------------------------------------------- ; Load 32 bit phase value from ROM ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R30 = index ;Out: R19:R18:R17:R16 = value ;Used: SREG,R0,R30,R31 ;----------------------------------------------------------------------------- LOAD_32BIT: lsl R30 ;R30 *= 2 ldi R31,0 adiw R30,DELTA_C ;Z = ROM address add R30,R30 adc R31,R31 lpm mov R16,R0 adiw R30,1 lpm mov R17,R0 adiw R30,1 lpm mov R18,R0 adiw R30,1 lpm mov R19,R0 ret ;----------------------------------------------------------------------------- ; Load phase delta from ROM ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R23,R22 = indeks = 0,0..12,0 = n,f ;Out: R19:R18:R17:R16 = delta ;Used: SREG,R0,R21,R24-R31 ;----------------------------------------------------------------------------- LOAD_DELTA: push R22 push R23 mov R30,R23 rcall LOAD_32BIT mov R24,R16 mov R25,R17 mov R26,R18 mov R27,R19 ;R27-R24 = delta[n] mov R30,R23 inc R30 rcall LOAD_32BIT sub R16,R24 sbc R17,R25 sbc R18,R26 sbc R19,R27 push R24 push R25 push R26 push R27 mov R23,R22 ldi R22,0 push R20 rcall MUL32X16 pop R20 pop R27 pop R26 pop R25 pop R24 add R16,R24 adc R17,R25 adc R18,R26 adc R19,R27 pop R23 pop R22 ret ;----------------------------------------------------------------------------- ;note number recalculation ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R23 = n 0..139 = m12 + 12*n12 ;Out: R23 = m12 0..11 ; R20 = n12 0..11 ;Used: SREG ;----------------------------------------------------------------------------- NOTERECALC: ldi R20,0 ;n12 = 0 rjmp NRC_2 NRC_1: subi R23,12 ;m12 -= 12 inc R20 ;n12++ NRC_2: cpi R23,12 brsh NRC_1 ;repeat while m12 >= 12 ret ;----------------------------------------------------------------------------- ;read a byte from a table ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R16 = i 0..255 ; R31:R30 = &Tab ;Out: R0 = Tab[i] 0..255 ;Used: SREG,R30,R31 ;----------------------------------------------------------------------------- TAB_BYTE: add R30,R30 ;\ adc R31,R31 ;/ Z = 2 * &Tab add R30,R16 adc R31,ZERO lpm ret ;----------------------------------------------------------------------------- ;read a word from a table ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R16 = i 0..255 ; R31:R30 = &Tab ;Out: R19:R18 = Tab[i] 0..65535 ;Used: SREG,R0,R30,R31 ;----------------------------------------------------------------------------- TAB_WORD: add R30,R16 adc R31,ZERO add R30,R30 ;\ adc R31,R31 ;/ Z = 2 * &Tab lpm mov R18,R0 ;LSByte adiw R30,1 ;Z++ lpm mov R19,R0 ;MSByte ret ;----------------------------------------------------------------------------- ;"time" --> "rate" conversion ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R16 = time 0..255 ;Out: R19:R18:R17:R16 = rate 0x001B0000..0xFFFF0000 ;Used: SREG,R0,R30,R31 ;----------------------------------------------------------------------------- ADCTORATE: lsr R16 lsr R16 lsr R16 ;0..31 ldi R30,TIMETORATE ldi R31,0 rcall TAB_WORD ;R19:R18 = rate clr R16 clr R17 ret ;----------------------------------------------------------------------------- ;conversion of the "detune B" potentiometer function ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;In: R16 = x 0..255 ;Out: R17,R16 = y 0,000..255,996 ;Used: SREG,R18-R30 ;----------------------------------------------------------------------------- NONLINPOT: ldi R22,0 mov R23,R16 cpi R23,112 brlo NLP_I cpi R23,144 brlo NLP_II rjmp NLP_III NLP_I: ldi R16,0 ;\ R18,R17:R16 = m = ldi R17,32 ; > = 126/112 = ldi R18,1 ;/ = 1,125 ldi R30,0 ;\ R31,R30 = n = ldi R31,0 ;/ = 0,0 rjmp NLP_CONT NLP_II: ldi R16,8 ;\ R18,R17:R16 = m = ldi R17,33 ; > = (130-126)/(143-112) = ldi R18,0 ;/ = 0,129032258 ldi R30,140 ;\ R31,R30 = n = ldi R31,111 ;/ = 126 - m*112 = 111,5483871 rjmp NLP_CONT NLP_III: ldi R16,183 ;\ R18,R17:R16 = m = ldi R17,29 ; > = (255-130)/(255-143) = ldi R18,1 ;/ = 1,116071429 ldi R30,103 ;\ R31,R30 = n = ldi R31,226 ;/ 255 - m*255 = -29,59821429 NLP_CONT: ldi R19,0 rcall MUL32X16 add R16,R30 adc R17,R31 ret ;------------------------------------------------------------------------------------------------------------------- ; M A I N P R O G R A M ;------------------------------------------------------------------------------------------------------------------- RESET: cli ;disable interrupts ;initialise stack: ldi R16,0x5F ;\ ldi R17,0x02 ; \ out SPL,R16 ; / SP = 0x025F out SPH,R17 ;/ ;initialise variables: clr ZERO clr PHASEA_0 clr PHASEA_1 clr PHASEA_2 clr PHASEB_0 clr PHASEB_1 clr PHASEB_2 clr YN1_L ;\ clr YN1_H ;/ y[n-1] = 0,0 ldi R16,0 sts GATE,R16 ;GATE = 0 sts GATEEDGE,R16 ;GATEEDGE = 0 sts LEVEL,R16 ;LEVEL = 0 sts ENV_FRAC_L,R16 ;\ sts ENV_FRAC_H,R16 ; > ENV = 0 sts ENV_INTEGR,R16 ;/ sts ADC_CHAN,R16 ;ADC_CHAN = 0 sts NOTE_L,R16 ;\ sts NOTE_H,R16 ; > sts NOTE_INTG,R16 ;/ sts MIDIPBEND_L,R16 ;\ sts MIDIPBEND_H,R16 ;/ P.BEND = 0 sts MIDIMODWHEEL,R16 ;MOD.WHEEL = 0 ldi R16,2 sts PORTACNT,R16 ;PORTACNT = 2 ldi R16,255 sts LPF_I,R16 ;no DCF sts MIDINOTE,R16 ;note# = 255 sts MIDINOTEPREV,R16 ;note# = 255 ldi R16,0x5E ;\ ldi R17,0xB4 ; \ ldi R18,0x76 ; \ initialising of sts SHIFTREG_0,R16 ; / shift register sts SHIFTREG_1,R17 ; / sts SHIFTREG_2,R18 ;/ ldi R16,0 ;\ ldi R17,0 ; > Amin = 0 ldi R18,0 ;/ sts LFOBOTTOM_0,R16 ;\ sts LFOBOTTOM_1,R17 ; > store Amin sts LFOBOTTOM_2,R18 ;/ ldi R16,255 ;\ ldi R17,255 ; > Amax = 255,999 ldi R18,255 ;/ sts LFOTOP_0,R16 ;\ sts LFOTOP_1,R17 ; > store Amax sts LFOTOP_2,R18 ;/ ;initialise sound parameters: ldi R16,0 sts LFOPHASE,R16 ; sts ENVPHASE,R16 ; sts DETUNEB_FRAC,R16 ;\ sts DETUNEB_INTG,R16 ;/ detune = 0 sts LFOLEVEL,R16 ; sts VCFENVMOD,R16 ; ldi R16,84 ;\ sts LFOFREQ,R16 ;/ ldi R16,0x18 ;\ sts MODEFLAGS1,R16 ;/ DCO B = on, DCA = env ldi R16,0x10 ;\ LFO = DCO sts MODEFLAGS2,R16 ;/ ENV mode: A-S-R ldi R16,128 ;\ sts ATTACKTIME,R16 ; > sts RELEASETIME,R16 ;/ ;initialise port A: ldi R16,0x00 ;\ out PORTA,R16 ;/ PA = zzzzzzzz ldi R16,0x00 ;\ out DDRA,R16 ;/ PA = iiiiiiii ;initialise port B: ldi R16,0xFF ;\ out PORTB,R16 ;/ PB = pppppppp ldi R16,0x00 ;\ out DDRB,R16 ;/ PB = iiiiiiii ;initialise port C: ldi R16,0x00 ;\ out PORTC,R16 ;/ PC = 00000000 ldi R16,0xFF ;\ out DDRC,R16 ;/ PC = oooooooo ;initialise port D: ldi R16,0xFC ;\ out PORTD,R16 ;/ PD = 1111110z ldi R16,0xFE ;\ out DDRD,R16 ;/ PD = oooooooi ;initialise Timer0: ldi R16,0x00 ;\ out TCCR0,R16 ;/ stop Timer 0 ;initialise Timer1: ldi R16,0x04 ;\ prescaler = CK/256 out TCCR1B,R16 ;/ (clock = 32µs) ;initialise Timer2: ldi R16,31 ;\ out OCR2,R16 ;/ OCR2 = 31 ldi R16,0x0A ;\ clear timer on compare, out TCCR2,R16 ;/ set prescaler = CK/8 ;initialise UART: ldi R16,15 ;(8MHz/(31,25kHz*16) - 1 out UBRR,R16 ;BAUD = 31,25 kBaud ldi R16,0x90 ;\ out UCR,R16 ;/ RXCIE=1, RXEN=1 ;initialise ADC: ldi R16,0x86 ;\ out ADCSR,R16 ;/ ADEN=1, clk = 125 kHz ;initialise interrupts: ldi R16,0x80 ;\ out TIMSK,R16 ;/ OCIE2=1 sei ;Interrupt Enable ;start conversion of the first A/D channel: lds R18,ADC_CHAN rcall ADC_START ;initialize the keyboard scan time 050419: in R16,TCNT1L ;\ in R17,TCNT1H ;/ R17:R16 = TCNT1 = t sts TPREV_KBD_L,R16 sts TPREV_KBD_H,R17 ;main loop: MAINLOOP: ;------------- ;switches: ;------------- ;050419 begin: in R16,TCNT1L ;\ in R17,TCNT1H ;/ R17:R16 = t lds R18,TPREV_KBD_L ;\ lds R19,TPREV_KBD_H ;/ R19:R18 = t0 sub R16,R18 ;\ sbc R17,R19 ;/ R17:R16 = t - t0 subi R16,LOW(KBDSCANPERIOD) ;\ sbci R17,HIGH(KBDSCANPERIOD) ;/ R17:R16 = (t-t0) - 100ms brsh MLP_SCAN ;\ rjmp MLP_SKIPSCAN ;/ skip scanning if (t-t0) < 100ms MLP_SCAN: in R16,TCNT1L in R17,TCNT1H sts TPREV_KBD_L,R16 ;\ sts TPREV_KBD_H,R17 ;/ t0 = t ;050419 end: ;reading: ldi R16,0x10 ;inverted state of PD outputs ldi R17,0x01 ;mask ldi R18,0x10 ;mask ldi R19,0x00 ;bits of SWITCH1 ldi R20,0x00 ;bits of SWITCH2 ldi R21,0x00 ;bits of SWITCH3 050505 MLP_SWLOOP: in R30,PORTD ori R30,0xF0 eor R30,R16 out PORTD,R30 rcall WAIT_10US ;wait 10-20µs 050421 in R30,PINB sbrs R30,0 ;\ or R19,R17 ;/ set bit when PB0==0 sbrs R30,1 ;\ or R19,R18 ;/ set bit when PB1==0 sbrs R30,2 ;\ or R20,R17 ;/ set bit when PB2==0 sbrs R30,3 ;\ or R20,R18 ;/ set bit when PB3==0 sbrs R30,4 ;\ or R21,R17 ;/ set bit when PB4==0 050505 lsl R17 ;R17 <<= 1 lsl R18 ;R18 <<= 1 lsl R16 ;R19 <<= 1 brne MLP_SWLOOP sts SWITCH1,R19 sts SWITCH2,R20 sts SWITCH3,R21 ; 050505 ;service: lds R16,SWITCH1 lds R17,SWITCH2 lds R18,MODEFLAGS1 lds R19,MODEFLAGS2 bst R16,0 ;\ bld R18,0 ;/ 4.0. bst R16,1 ;\ bld R19,5 ;/ 5.0. bst R16,2 ;\ bld R19,6 ;/ 6.0. LFO KBD SYNC off/on bst R16,3 ;\ bld R19,7 ;/ 7.0. LFO norm/rand bst R16,4 ;\ bld R19,3 ;/ 4.1. ENV LOOP off/on bst R16,5 ;\ bld R18,5 ;/ 5.1. transpose down/up bst R16,6 ;\ bld R18,6 ;/ 6.1. octave A down/up bst R16,7 ;\ bld R18,7 ;/ 7.1. octave B down/up bst R17,0 ;\ bld R18,1 ;/ 4.2. wave A saw/squ bst R17,1 ;\ bld R18,2 ;/ 5.2. wave B saw/squ bst R17,2 ;\ bld R18,3 ;/ 6.2. DCO B off/on bst R17,3 ;\ bld R18,4 ;/ 7.2. DCA gate/env bst R17,4 ;\ bld R19,4 ;/ 4.3. ENV AR/ASR bst R17,5 ;\ bld R19,0 ;/ 5.3. LFO DCO/DCF bst R17,6 ;\ bld R19,1 ;/ 6.3. LFO TRI/SQU bst R17,7 ;\ bld R19,2 ;/ 7.3. DCF KBD TRACK sts MODEFLAGS1,R18 sts MODEFLAGS2,R19 ;MIDI channel will be set: 050505 lds R16,SWITCH3 sts SETMIDICHANNEL,R16 ;0 for OMNI or 1..15 MLP_SKIPSCAN: ;----- ;potentiometers: ;----- ;reading: rcall ADC_END ;R16 = AD(i) lds R18,ADC_CHAN ;\ ldi R28,ADC_0 ; \ add R28,R18 ; / Y = &ADC_i ldi R29,0 ;/ st Y,R16 ;AD(i) --> ADC_i ;next channel: inc R18 andi R18,0x07 sts ADC_CHAN,R18 rcall ADC_START ;start conversion of next channel ;service: lds R16,ADC_0 ;\ sts LFOFREQ,R16 ;/ AD0 --> LFO rate lds R16,ADC_1 ;\ sts LFOLEVEL,R16 ;/ AD1 --> LFO level lds R16,ADC_2 ;\ rcall NONLINPOT ; \ subi R17,128 ; > AD2 --> tune B sts DETUNEB_FRAC,R16 ; / -128,000..+127,996 sts DETUNEB_INTG,R17 ;/ lds R16,ADC_3 ;\ sts CUTOFF,R16 ;/ AD3 --> DCF cutoff lds R16,ADC_4 ;\ sts ATTACKTIME,R16 ;/ AD4 --> ATTACK TIME lds R16,ADC_5 ;\ sts RELEASETIME,R16 ;/ AD5 --> RELEASE TIME lds R16,ADC_6 ;\ sts PORTAMENTO,R16 ;/ AD6 --> PORTAMENTO lds R16,ADC_7 ;\ sts VCFENVMOD,R16 ;/ AD7 --> DCF ENV MOD ;---------- ;calculate dT: ;---------- in R22,TCNT1L ;\ in R23,TCNT1H ;/ R23:R22 = TCNT1 = t mov R18,R22 ;\ mov R19,R23 ;/ R19:R18 = t lds R16,TPREV_L ;\ lds R17,TPREV_H ;/ R17:R16 = t0 sub R22,R16 ;\ R23:R22 = t - t0 = dt sbc R23,R17 ;/ (1 bit = 32 µs) sts TPREV_L,R18 ;\ sts TPREV_H,R19 ;/ t0 = t sts DELTAT_L,R22 ;\ sts DELTAT_H,R23 ;/ R23:R22 = dT ;---- ;LFO: ;---- ;calculate dA: lds R16,LFOFREQ ;\ com R16 ;/ R16 = 255 - ADC0 rcall ADCTORATE ;R19:R18:R17:R16 = rate of rise/fall lds R22,DELTAT_L ;\ lds R23,DELTAT_H ;/ R23:R22 = dT rcall MUL32X16 ;R18:R17:R16 = dA lds R19,LFO_FRAC_L lds R20,LFO_FRAC_H lds R21,LFO_INTEGR subi R21,128 ldi R31,0 ;flag = 0 lds R30,LFOPHASE tst R30 brne MLP_LFOFALL ;rising phase: MLP_LFORISE: lds R22,LFOTOP_0 ;\ lds R23,LFOTOP_1 ; > R24:R23:R22 = Amax lds R24,LFOTOP_2 ;/ add R19,R16 ;\ adc R20,R17 ; > A += dA adc R21,R18 ;/ brcs MLP_LFOTOP cp R19,R22 ;\ cpc R20,R23 ; > A - Amax cpc R21,R24 ;/ brlo MLP_LFOX ;skip when A < Amax ;A reached top limit: MLP_LFOTOP: mov R19,R22 ;\ mov R20,R23 ; > A = Amax mov R21,R24 ;/ ldi R30,1 ;begin of falling ldi R31,1 ;flag = 1 rjmp MLP_LFOX ;falling phase: MLP_LFOFALL: lds R22,LFOBOTTOM_0 ;\ lds R23,LFOBOTTOM_1 ; > R24:R23:R22 = Amin lds R24,LFOBOTTOM_2 ;/ sub R19,R16 ;\ sbc R20,R17 ; > A -= dA sbc R21,R18 ;/ brcs MLP_LFOBOTTOM cp R22,R19 ;\ cpc R23,R20 ; > Amin - A cpc R24,R21 ;/ brlo MLP_LFOX ;skip when A > Amin ;A reached bottom limit: MLP_LFOBOTTOM: mov R19,R22 ;\ mov R20,R23 ; > A = Amin mov R21,R24 ;/ ldi R30,0 ;begin of rising ldi R31,1 ;flag = 1 MLP_LFOX: sts LFOPHASE,R30 subi R21,128 ;R21,R20:R19 = LFO tri wave sts LFO_FRAC_L,R19 ;\ sts LFO_FRAC_H,R20 ; > store LFO value sts LFO_INTEGR,R21 ;/ ;switch norm/rand: ;determine Amin i Amax: ldi R16,0 ;\ ldi R17,0 ; > Amin when not LFO==tri ldi R18,0 ;/ and not LFO==rand lds R30,MODEFLAGS2 andi R30,0x82 cpi R30,0x80 ;Z = ((LFO==tri)&&(LFO==rand)) brne MLP_LFOAWR tst R31 breq MLP_LFOAX lds R16,SHIFTREG_0 ;\ lds R17,SHIFTREG_1 ; \ Amin = pseudo-random number lds R18,SHIFTREG_2 ; / 0,000..127,999 andi R18,0x7F ;/ MLP_LFOAWR: sts LFOBOTTOM_0,R16 ;\ sts LFOBOTTOM_1,R17 ; > store Amin sts LFOBOTTOM_2,R18 ;/ com R16 ;\ com R17 ; > Amax = 255,999 - Amin com R18 ;/ 128,000..255,999 sts LFOTOP_0,R16 ;\ sts LFOTOP_1,R17 ; > store Amax sts LFOTOP_2,R18 ;/ MLP_LFOAX: lds R16,MODEFLAGS2 andi R16,0x82 cpi R16,0x82 ;Z = ((LFO==squ)&&(LFO==rand)) brne MLP_LFONORM tst R31 ;flag == 1 ? breq MLP_LFONWR ;jump when not lds R21,SHIFTREG_2 rjmp MLP_LFOWR MLP_LFONORM: ;switch tri/squ: lds R16,MODEFLAGS2 ;\ Z=0: squ andi R16,0x02 ;/ Z=1: tri breq MLP_LFOWR lsl R21 ;Cy = (LFO < 0) ldi R21,127 ;\ adc R21,ZERO ;/ R21 = -128 or +127 MLP_LFOWR: sts LFOVALUE,R21 MLP_LFONWR: lds R21,LFOVALUE ;determine LFO level: ;; lds R16,MODEFLAGS2 ;; andi R16,0x20 ;; breq MLP_LFOMWX ;skip when MOD.WHEEL = off lds R16,LFOLEVEL lds R17,MIDIMODWHEEL cp R16,R17 brsh MLP_LFOLWR mov R16,R17 ;M.WHEEL is greater MLP_LFOLWR: sts LFOLEVEL,R16 MLP_LFOMWX: ;---- ;ENV: ;---- ;check envelope phase: lds R17,ENVPHASE lds R16,ATTACKTIME cpi R17,1 breq MLP_ENVAR ;when "attack" lds R16,RELEASETIME cpi R17,3 breq MLP_ENVAR ;when "release" rjmp MLP_EEXIT ;when "stop" or "sustain" ;calculate dL: MLP_ENVAR: rcall ADCTORATE ;R19:R18:R17:R16 = rate of rise/fall lds R22,DELTAT_L ;\ lds R23,DELTAT_H ;/ R23:R22 = dT rcall MUL32X16 ;R18:R17:R16 = dL ;add/subtract dL to/from L: lds R19,ENV_FRAC_L ;\ lds R20,ENV_FRAC_H ; > R21:R20:R19 = L lds R21,ENV_INTEGR ;/ lds R22,ENVPHASE cpi R22,3 breq MLP_ERELEASE MLP_EATTACK: add R19,R16 ;\ adc R20,R17 ; > R21:R20:R19 = L + dL adc R21,R18 ;/ brcc MLP_ESTORE ;L reached top limit: ldi R19,255 ;\ ldi R20,255 ; > L = Lmax ldi R21,255 ;/ ldi R16,3 ;when A-R or loop: release lds R17,MODEFLAGS2 ;\ bst R17,3 ;/ T = 0 (norm) / 1 (loop) brts MLP_ESTOREP lds R17,MODEFLAGS2 ;\ bst R17,4 ;/ T = 0 (AR) / 1 (ASR) brtc MLP_ESTOREP ldi R16,2 ;when A-S-R and norm: sustain rjmp MLP_ESTOREP MLP_ERELEASE: sub R19,R16 ;\ sbc R20,R17 ; > R21:R20:R19 = L - dL sbc R21,R18 ;/ brcc MLP_ESTORE ;L reached bottom limit: ldi R19,0 ;\ ldi R20,0 ; > L = 0 ldi R21,0 ;/ ldi R16,1 ;when loop: attack lds R17,MODEFLAGS2 ;R17.3 = 0 (norm) / 1 (loop) sbrs R17,3 ;skip when loop ldi R16,0 ;stop MLP_ESTOREP: sts ENVPHASE,R16 ;store phase MLP_ESTORE: sts ENV_FRAC_L,R19 ;\ sts ENV_FRAC_H,R20 ; > store L sts ENV_INTEGR,R21 ;/ MLP_EEXIT: ;----- ;GATE: ;----- lds R16,GATE tst R16 ;check GATE brne MLP_KEYON ;no key is pressed: MLP_KEYOFF: ldi R16,3 ;\ sts ENVPHASE,R16 ;/ "release" rjmp MLP_NOTEON ;key is pressed: MLP_KEYON: lds R16,GATEEDGE tst R16 ;Z=0 when key has just been pressed breq MLP_NOTEON ;key has just been pressed: ldi R16,0 ;\ sts GATEEDGE,R16 ;/ GATEEDGE = 0 lds R16,PORTACNT ;\ tst R16 ; \ breq MLP_KEYON1 ; > if ( PORTACNT != 0 ) dec R16 ; / PORTACNT-- sts PORTACNT,R16 ;/ MLP_KEYON1: ;envelope starts: ldi R16,1 ;\ sts ENVPHASE,R16 ;/ attack ldi R16,0 sts ENV_FRAC_L,R16 ;\ sts ENV_FRAC_H,R16 ; > ENV = 0 sts ENV_INTEGR,R16 ;/ ; LFO starts (only when LFO KBD SYNC = on): lds R16,MODEFLAGS2 andi R16,0x40 breq MLP_NOTEON ;skip when LFO KBD SYNC = off ldi R16,255 ;\ ldi R17,255 ; > A = Amax ldi R18,127 ;/ sts LFO_FRAC_L,R16 ;\ sts LFO_FRAC_H,R17 ; > store A sts LFO_INTEGR,R18 ;/ ldi R16,1 ;\ sts LFOPHASE,R16 ;/ begin of falling MLP_NOTEON: ;------------- ;DCO A, DCO B: ;------------- ldi R25,0 ;\ ldi R22,0 ; > R23,R22:R25 = note# 0..127 lds R23,MIDINOTE ;/ cpi R23,255 brne MLP_NLIM2 rjmp MLP_VCOX ;note# limited to 36..96: MLP_NLIM1: subi R23,12 MLP_NLIM2: cpi R23,97 brsh MLP_NLIM1 rjmp MLP_NLIM4 MLP_NLIM3: subi R23,244 MLP_NLIM4: cpi R23,36 brlo MLP_NLIM3 ;transpose 1 octave down: subi R23,12 ;n -= 12 24..84 ;portamento: lds R25,NOTE_L ;\ lds R26,NOTE_H ; > R27,R26:R25 = nCurr lds R27,NOTE_INTG ;/ lds R16,PORTACNT ;\ tst R16 ; > jump when it's the first note brne MLP_PORTAWR ;/ (PORTACNT != 0) lds R16,PORTAMENTO rcall ADCTORATE push R22 push R23 mov R22,R18 ;\ R23:R22 = portamento rate mov R23,R19 ;/ 65535..27 ldi R16,0 ldi R17,0 lds R18,DELTAT_L lds R19,DELTAT_H ldi R20,3 rcall SHR32 rcall MUL32X16 ;R18,R17:R16 = nDelta pop R23 pop R22 mov R19,R16 ;\ mov R20,R17 ; > R21,R20:R19 = nDelta mov R21,R18 ;/ lds R25,NOTE_L ;\ lds R26,NOTE_H ; > R27,R26:R25 = nCurr lds R27,NOTE_INTG ;/ cp R22,R26 ;\ nEnd - nCurr cpc R23,R27 ;/ Cy = (nEnd < nCurr) brsh MLP_PORTAADD MLP_PORTAMIN: sub R25,R19 ;\ sbc R26,R20 ; > nCurr -= nDelta sbc R27,R21 ;/ cp R22,R26 ;\ nEnd - nCurr; cpc R23,R27 ;/ Cy = (nEnd < nCurr) brlo MLP_PORTA1 rjmp MLP_PORTAEND MLP_PORTAADD: add R25,R19 ;\ adc R26,R20 ; > nCurr += nDelta adc R27,R21 ;/ cp R22,R26 ;\ nEnd - nCurr; cpc R23,R27 ;/ Cy = (nEnd < nCurr) brsh MLP_PORTA1 MLP_PORTAEND: ldi R25,0 ;\ mov R26,R22 ; > nCurr = nEnd mov R27,R23 ;/ MLP_PORTA1: mov R22,R26 mov R23,R27 MLP_PORTAWR: sts NOTE_L,R25 sts NOTE_H,R22 sts NOTE_INTG,R23 ;"transpose" switch: lds R16,MODEFLAGS1 ;b5 = transpose: 0=down, 1=up sbrs R16,5 ;\ 24..96 subi R23,12 ;/ n -= 12 12..84 ;pitch bender (-12..+12): ;; lds R16,MODEFLAGS2 ;; andi R16,0x10 ;; breq MLP_PBX ;skip when P.BEND = off lds R16,MIDIPBEND_L ;\ R17,R16 = P.BEND lds R17,MIDIPBEND_H ;/ -128,000..+127,996 ldi R18,5 ;\ R17,R16 = P.BEND/32 rcall ASR16 ;/ -4,000..+3,999 mov R18,R16 ;\ R19,R18 = P.BEND/32 mov R19,R17 ;/ -4,000..+3,999 add R16,R18 ;\ R17,R16 = 2/32*P.BEND adc R17,R19 ;/ -8,000..+7,999 add R16,R18 ;\ R17,R16 = 3/32*P.BEND adc R17,R19 ;/ -12,000..+11,999 add R22,R16 ;\ adc R23,R17 ;/ add P.BEND MLP_PBX: ;for "DCF KBD TRACK": sts PITCH,R23 ;n = 0..108 ;LFO modulation: lds R16,MODEFLAGS2 ;\ andi R16,0x01 ;/ Z=1 when LFO=DCO brne MLP_VCOLFOX ;skip when LFO=DCF lds R16,LFOVALUE ;R16 = LFO -128..+127 lds R17,LFOLEVEL ;R17 = LFO level 0..255 ;nonlinear potentiometer function: mov R18,R17 ;R18 = LL lsr R17 ;R17 = LL/2 cpi R18,128 brlo MLP_OM1 ;skip if LL = 0..127 subi R17,128 ;R17 = 0,5*LL-128 -64..-1 add R17,R18 ;R17 = 1,5*LL-128 +64..254 MLP_OM1: rcall MUL8X8S ;R17,R16 = LFO*mod ldi R18,4 ;\ rcall ASR16 ;/ R17,R16 = LFO*mod / 16 add R22,R16 ;\ adc R23,R17 ;/ add LFO to note # ;limiting to 0..108 tst R23 brpl MLP_VCOLFO1 ldi R22,0 ldi R23,0 rjmp MLP_VCOLFOX MLP_VCOLFO1: cpi R23,109 brlo MLP_VCOLFOX ldi R22,0 ldi R23,108 MLP_VCOLFOX: push R22 ;\ note# = 0..108 push R23 ;/ store for phase delta B ;phase delta A: ;octave A: lds R16,MODEFLAGS1 ;b6 = octave A: 0=down, 1=up sbrc R16,6 subi R23,244 ;n += 12 rcall NOTERECALC ;R23,R22 = m12 (0,0..11,996), ;R20 = n12 (0..11) rcall LOAD_DELTA ;R19:R18:R17:R16 = delta rcall SHL32 ;R19:R18:R17:R16 = delta*(2^exp) mov DELTAA_0,R17 mov DELTAA_1,R18 mov DELTAA_2,R19 ;phase delta B: pop R23 ;\ pop R22 ;/ n ;detune B: lds R16,DETUNEB_FRAC ;\ R17,R16 = detuneB lds R17,DETUNEB_INTG ;/ -128,000..+127,996 ldi R18,4 ;\ R17,R16 = detuneB / 16 rcall ASR16 ;/ -8,0000..+7,9998 add R22,R16 ;\ adc R23,R17 ;/ ;octave B: lds R16,MODEFLAGS1 ;b7 = octave B: 0=down, 1=up sbrc R16,7 subi R23,244 ;n += 12 rcall NOTERECALC ;R23,R22 = m12 (0,0..11,996), ;R20 = n12 (0..11) rcall LOAD_DELTA ;R19:R18:R17:R16 = delta rcall SHL32 ;R19:R18:R17:R16 = delta*(2^exp) mov DELTAB_0,R17 mov DELTAB_1,R18 mov DELTAB_2,R19 MLP_VCOX: ;---- ;DCF: ;---- ;LFO mod: ldi R30,0 ;\ ldi R31,0 ;/ sum = 0 lds R16,MODEFLAGS2 ;\ andi R16,0x01 ;/ Z=1 when LFO=DCO breq MLP_DCF0 ;skip when LFO=DCO lds R16,LFOVALUE ;R16 = LFO -128..+127 lds R17,LFOLEVEL ;R17 = DCF LFO MOD 0..255 rcall MUL8X8S ;R17,R16 = LFO * VCFLFOMOD mov R30,R17 ldi R31,0 rol R17 ;R17.7 --> Cy (sign) sbc R31,R31 ;sign extension to R31 MLP_DCF0: ;ENV mod: lds R16,ENV_INTEGR lds R17,VCFENVMOD rcall MUL8X8U ;R17,R16 = ENV * ENVMOD rol R16 ;Cy = R16.7 (for rounding) adc R30,R17 adc R31,ZERO ;KBD TRACK: lds R16,MODEFLAGS2 ;\ Z=0: KBD TRACK on andi R16,0x04 ;/ Z=1: KBD TRACK off breq MLP_DCF3 lds R16,PITCH ;R16 = n (12/octave) 0..96 lsl R16 ;R16 = 2*n (24/octave) 0..192 subi R16,96 ;R16 = 2*(n-48) (24/octave) -96..+96 ldi R17,171 rcall MUL8X8S ;R17 = 1,5*(n-48) (16/octave) -64..+64 ldi R18,0 ;\ sbrc R17,7 ; > R18 = sign extension ldi R18,255 ;/ of R17 add R30,R17 adc R31,R18 MLP_DCF3: ;CUTOFF: lds R16,CUTOFF clr R17 add R16,R30 adc R17,R31 tst R17 brpl MLP_DCF1 ldi R16,0 rjmp MLP_DCF2 MLP_DCF1: breq MLP_DCF2 ldi R16,255 MLP_DCF2: lsr R16 lsr R16 ;0..63 ldi R30,TAB_VCF ;\ ldi R31,0 ;/ Z = &Tab rcall TAB_BYTE ;R0 = 1..255 sts LPF_I,R0 ;--------------- ;sound level: ;--------------- lds R17,MODEFLAGS1 ;\ check DCA mode: andi R17,0x10 ;/ Z=1 (gate), Z=0 (env) brne MLP_VCAENV ;jump when mode==env lds R16,GATE ;\ ror R16 ;/ GATE --> Cy ldi R16,0 ;\ R16 = 0 (when GATE == 0), sbc R16,R16 ;/ R16 = 255 (when GATE == 1) rjmp MLP_VCAOK MLP_VCAENV: lds R16,ENV_INTEGR ldi R17,46 rcall MUL8X8U mov R16,R17 ;R16 = 0..45 ldi R30,TAB_VCA ;\ ldi R31,0 ;/ Z = &Tab rcall TAB_BYTE ;R0 = 2..255 mov R16,R0 MLP_VCAOK: sts LEVEL,R16 ;----------------------------------- ;pseudo-random shift register: ;----------------------------------- ;BIT = SHIFTREG.23 xor SHIFTREG.18 ;SHIFTREG = (SHIFTREG << 1) + BIT lds R16,SHIFTREG_0 lds R17,SHIFTREG_1 lds R18,SHIFTREG_2 bst R18,7 ;\ bld R19,0 ;/ R19.0 = SHIFTREG.23 bst R18,2 ;\ bld R20,0 ;/ R20.0 = SHIFTREG.18 eor R19,R20 ;R19.0 = BIT lsr R19 ;Cy = BIT rol R16 ;\ rol R17 ; > R18:R17:R16 = rol R18 ;/ = (SHIFTREG << 1) + BIT sts SHIFTREG_0,R16 sts SHIFTREG_1,R17 sts SHIFTREG_2,R18 ;------------------------ ;back to the main loop: ;------------------------ rjmp MAINLOOP ;-------------------------------------------------------------------------------------------------------------------