title "Programmable Voltage-Controlled PWM LFO" ; ; This program provides a versatile LFO on a single chip. ; Analogue output is provided as a PWM output, which requires LP ; filtering to be useable. ; ; Hardware Notes: ; PIC16F684 running at 20 MHz using an external clock ; RA0/AN0: 0-5V LFO Frequency CV ; RA1/AN1: 0-5V Waveform control ; Only the top three bits are used, and the waves are encoded as follows: ; 0-Ramp Up ; 1-Ramp Down ; 2-Pulse ; 3-Triangle ; 4-Sine ; 5-Sweep wave ("logarithmic" sweep) ; 6-Spike wave ; 7-Noise ; RA2/AN2: 0-5V Step Rate control ; RC0/AN4: 0-5V Output Level control ; RC1/AN5: 0-5V Phase Distortion CV ; RC2/AN6: 0-5V Sync In Mode Select ; Only the top 2 bits are used, and the modes are encoded as follows: ; 0-Sync Input Disabled ; 1-Sync Input resets S&H ; 2-Sync Input resets LFO ; 3-Sync Input resets S&H and LFO ; RC3/AN7: 0-5V Sync Out Frequency Select ; Only the top 2 bits are used, and the outputs are as follows: ; 0-LFO Freq ; 1-LFO Freq x 2 ; 2-LFO Freq x 4 ; 3-LFO Freq x 8 ; RC4 : Sync Output (Outputs pulses at rate set by RC3/AN7) ; RC5 : PWM LFO Output ; RA3 : Sync Input (Digital input that resets the LFO and/or S&H) ; ; ; ; PWMLFO3 - Experimenting with external 20MHz clock and multiple ; CV inputs. ; ; PWMLFO6 - Adding SYNC IN.I've also sorted the hysteresis on the STEP_CV and WAVE_CV ; PWMLFO7 - Adding more detail to the waveforms that need it ; I've now squeezed about as much resolution out of it as I can ; PWMLFO8 - Adding SYNC OUT and SYNC OUT FREQ CV. ; PWMLFO9 - Replacing PWM CV with more general Phase Distortion ; 20/1/07 - Made this work. The trouble was making the ; division fit. SYNC OUT has been removed to make way. ; Also moved S&H rate up by an octave over the whole range. I never ; found myself playing with the slow rates. LIST R=DEC INCLUDE "p16f684.inc" __CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC ;------------------------------ ; Variables ;------------------------------ CBLOCK 0x020 ; Registers for context saving during interrupts W_TEMP STATUS_TEMP PCLATH_TEMP FSR_TEMP ; General working storage TEMP ; Used for the AD delay WAVETEMP; The sweep wave and sine lookup need temp register WAVETEMP2; The Spike wave lookups needs 2 temp registers LOOKUPTEMP; The FREQ_INC lookup needs a temp register too NOISETEMP; Noise source needs temp storage for XOR output SYNCTEMP ; The Sync Out routine needs a temp register INCTEMP_HI ; Used by UpdateFreqIncs to store the raw INCTEMP_MID ; Freq Inc value when it has been x128 INCTEMP_LO DIVTEMP ; Used as a counter in the Division routine ; The current A/D channel and value ADC_CHANNEL ADC_VALUE ; The S&H feature STEP_MULT ; Time is reduced by a multipe of 16 for the S&H STEP_HI ; This is the current STEP count STEP_LO STEP_RATE_HI ; This is the current STEP_RATE, the max value of STEP STEP_RATE_LO ; It stands in instead of "STEP_CV" ; The 3 bit waveform select value WAVE ; Various flag bits. 1 is on,obviously. FLAGS ; See Define statements below for details ; The 32 bit noise source shift register SHIFT_REG0 SHIFT_REG1 SHIFT_REG2 SHIFT_REG3 ; The 24 bit phase accumulator PHASE_HI PHASE_MID PHASE_LO ; The raw 16 bit frequency increment FREQ_INC_HI ; This is the raw reading from the table FREQ_INC_LO ; The actual 24 bit phase distortion increments FREQ_INC_A_HI ; This is the inc for the first half of the wave FREQ_INC_A_MID FREQ_INC_A_LO FREQ_INC_B_HI ; This is the inc for the 2nd half of the wave FREQ_INC_B_MID FREQ_INC_B_LO ; The current output level CV LEVEL_CV ; The current phase distortion CV DISTORT_CV ; The Sync In/Out options SYNC_IN_MODE SYNC_OUT_FREQ ; The 16 bit output level (after it's been scaled by LEVEL_CV) OUTPUT_HI OUTPUT_LO ; The offset that gets added to the output to ensure it is ; bipolar and centred around 128 OFFSET_HI OFFSET_LO ; Late addition - The division routine for Phase Distortion ; INDEX_HI ; INDEX_MID ; INDEX_LO ; DIVISOR_HI ; DIVISOR_MID ; DIVISOR_LO NUMBER_HI NUMBER_MID NUMBER_LO REMAIN DIVISOR ; REMAINDER_HI ; REMAINDER_MID ; REMAINDER_LO ; RESULT_HI ; RESULT_MID ; RESULT_LO ENDC ;------------------------------------- ; DEFINE STATEMENTS ;------------------------------------- ; Useful bit definitions for clarity #define ZEROBIT STATUS,Z ; Zero Flag #define CARRY STATUS,C ; Carry #define BORROW STATUS,C ; Borrow is the same as Carry ; Flag bit definitions #define STEP_ENABLE FLAGS, 0 ; S&H On/Off #define NOISE_ENABLE FLAGS, 1 ; Noise On/Off ; Input/Output bit definitions #define SYNC_IN PORTA, 3 ; Sync Input #define SYNC_OUT PORTC, 4 ; Sync Output ;---------------------------------------------------------------------- ; Begin Executable Code Segment ;---------------------------------------------------------------------- org 0x000 ; processor reset vector nop ; for ICD use goto Main ; Go to the main program org 0x004 ;Interrupt vector location InterruptEnter movwf W_TEMP ; save W register swapf STATUS, W ; swap status to be saved into W bcf STATUS, RP0 ; ---- Select Bank 0 ----- movwf STATUS_TEMP ; save STATUS register movfw PCLATH movwf PCLATH_TEMP ; save PCLATH register clrf PCLATH movfw FSR movwf FSR_TEMP ; save FSR register ;---------------------------------------- ; Interrupt Service Routine (ISR) (Section 12.4) ; This deals with the DDS and PWM output, ; and also the SYNC IN ;---------------------------------------- SyncISR btfss INTCON, RAIF ; Check if SYNC_IN pin has altered goto Timer2ISR movfw PORTA ; Read the port (necessary to clear the Int) bcf INTCON, RAIF ; Clear SYNC_IN interrupt flag ; (As a quirk, these last two lines MUST be in this order) ; If SYNC_IN has gone high, check if we need to reset anything btfss SYNC_IN goto Timer2ISR ; Otherwise, skip this bit SyncGoneHigh ; If it's gone high, we might need to reset the LFO and/or S&H ; (Note that this is a hard sync, not a replacement clock) ResetSampleAndHold ; Do we need to reset the S&H? btfss SYNC_IN_MODE, 6 goto ResetLFOWaveform movlw D'7' movwf STEP_MULT movlw D'1' movwf STEP_HI movwf STEP_LO ResetLFOWaveform ; Do we need to reset the LFO? btfss SYNC_IN_MODE, 7 goto Timer2ISR clrf PHASE_HI clrf PHASE_MID clrf PHASE_LO Timer2ISR btfss PIR1, TMR2IF ; check if TMR2 interrupt goto InterruptExit bcf PIR1, TMR2IF ; clear TMR2 interrupt flag ; Are we running the DDS or the Noise source? SelectDDSOrNoise btfsc NOISE_ENABLE goto NoiseSource ; Increment the DDS phase accumulator PHASE (24+24 bit addition) IncrementPhase ; Test the high bit in here and branch to one of two versions: ; SectionAincrement or SectionBIncrement btfsc PHASE_HI, 7 ; Are we onto the 2nd half of the wave? goto SectionBIncrement SectionAIncrement movf FREQ_INC_A_LO, w ; Add FREQ_INC_LO to PHASE_LO addwf PHASE_LO, f btfsc CARRY ; Do we carry one to the middle byte? incf PHASE_MID, f btfsc ZEROBIT ; Has that carry caused a carry to the high byte? incf PHASE_HI, f movf FREQ_INC_A_MID, w ; Add FREQ_INC_HI to PHASE_MID addwf PHASE_MID, f btfsc CARRY ; Do we carry one to the high byte? incf PHASE_HI, f movf FREQ_INC_A_HI, w ; Add FREQ_INC_HI to PHASE_HI addwf PHASE_HI, f goto SampleAndHold ; Skip the noise source SectionBIncrement movf FREQ_INC_B_LO, w ; Add FREQ_INC_LO to PHASE_LO addwf PHASE_LO, f btfsc CARRY ; Do we carry one to the middle byte? incf PHASE_MID, f btfsc ZEROBIT ; Has that carry caused a carry to the high byte? incf PHASE_HI, f movf FREQ_INC_B_MID, w ; Add FREQ_INC_HI to PHASE_MID addwf PHASE_MID, f btfsc CARRY ; Do we carry one to the high byte? incf PHASE_HI, f movf FREQ_INC_B_HI, w ; Add FREQ_INC_HI to PHASE_HI addwf PHASE_HI, f goto SampleAndHold ; Skip the noise source ; LEAVING THIS OUT!! ; Generate a sync output pulse at the required multiple of the LFO freq ;GenerateSyncOut ; comf PHASE_HI, w ; Inverted wrt the square wave, for some reason.. ; movwf SYNCTEMP ; bcf CARRY ;TestBitSeven ; If bit 7 of SYNC_OUT_FREQ is high, ; bitshift the SYNCTEMP byte left twice ; btfss SYNC_OUT_FREQ,7 ; goto TestBitSix ; rlf SYNCTEMP, f ; rlf SYNCTEMP, f ;TestBitSix ; If bit 6 of SYNC_OUT_FREQ is high, ; bitshift the SYNCTEMP byte once more ; btfss SYNC_OUT_FREQ,6 ; goto OutputSync ; rlf SYNCTEMP, f ;OutputSync ; btfss SYNCTEMP,7 ; bcf SYNC_OUT ; btfsc SYNCTEMP,7 ; bsf SYNC_OUT ; Bit-shift the tapped shift register noise source NoiseSource btfss NOISE_ENABLE ; Is the noise source enabled? goto SampleAndHold ;Get bits 32, 29, 6, and 4 for the feedback XOR ; Bit 32 -> NOISETEMP bsf NOISETEMP, 0 ; Assume it's set btfss SHIFT_REG3, 7 bcf NOISETEMP, 0 ; Clear it if not ; Invert the NOISETEMP bit if Bit 29 is set btfsc SHIFT_REG3, 4 comf NOISETEMP, f ; Invert the NOISETEMP bit if Bit 6 is set btfsc SHIFT_REG0, 5 comf NOISETEMP, f ; Invert the NOISETEMP bit if Bit 4 is set btfsc SHIFT_REG0, 3 comf NOISETEMP, f ; Feedback is inverted wrt the XOR output bsf CARRY ; Assume it's set btfsc NOISETEMP, 0 bcf CARRY ; Clear it if not rlf SHIFT_REG0, f ; 32 bit shift rlf SHIFT_REG1, f rlf SHIFT_REG2, f rlf SHIFT_REG3, f ; Provide Sample-and-Hold feature (Stepped wave) SampleAndHold btfss STEP_ENABLE ; Is S&H switched on? goto SelectWaveform ; Nope, it's not.. ; Time should be reduced by a multiple of 8 for the STEP_RATE incf STEP_MULT, f movf STEP_MULT, w andlw B'00000111' ; Only need the three LSBs btfss ZEROBIT goto InterruptExit ; We're still holding a value, so exit ; Decrement the 16-bit Step counter decf STEP_LO, f btfsc ZEROBIT decf STEP_HI, f btfss ZEROBIT ; If STEP hasn't reached zero, then just skip straight goto InterruptExit ; to the end without bothering to update the output ; This is a new step (a sample, not a hold) movf STEP_RATE_HI, w ; Set step for next time movwf STEP_HI movf STEP_RATE_LO, w movwf STEP_LO ; Which waveform table should we use? SelectWaveform movf WAVE, w ; Get current waveform addwf PCL, f ; Increment program counter with waveform value ; Note that I cleared PCLATH on entry to the ISR ; otherwise this computed goto would be risky goto RampUp goto RampDown goto Pulse goto Triangle goto Sine goto Sweep goto Spike goto Noise ReturnWithValue ; Modify the Output level ;------------------------- ; This involves multiplying the DDS LFO/Noise source output by ; the LEVEL_CV value - an 8 bit x 8 bit multiplication ; Multiply routine from Microchip App Note 26 ; Expects number to be multiplied by LEVEL_CV in W MultiplyByLevelCV clrf OUTPUT_HI clrf OUTPUT_LO clrc ; Clear carry? Why didn't I know about this?! btfsc LEVEL_CV,0 addwf OUTPUT_HI, f rrf OUTPUT_HI, f rrf OUTPUT_LO, f btfsc LEVEL_CV,1 addwf OUTPUT_HI, f rrf OUTPUT_HI, f rrf OUTPUT_LO, f btfsc LEVEL_CV,2 addwf OUTPUT_HI, f rrf OUTPUT_HI, f rrf OUTPUT_LO, f btfsc LEVEL_CV,3 addwf OUTPUT_HI, f rrf OUTPUT_HI, f rrf OUTPUT_LO, f btfsc LEVEL_CV,4 addwf OUTPUT_HI, f rrf OUTPUT_HI, f rrf OUTPUT_LO, f btfsc LEVEL_CV,5 addwf OUTPUT_HI, f rrf OUTPUT_HI, f rrf OUTPUT_LO, f btfsc LEVEL_CV,6 addwf OUTPUT_HI, f rrf OUTPUT_HI, f rrf OUTPUT_LO, f btfsc LEVEL_CV,7 addwf OUTPUT_HI, f rrf OUTPUT_HI, f rrf OUTPUT_LO, f ; Add an offset to ensure the LFO output is centred around 128 ; Work out the offset to add comf LEVEL_CV, w ; Offset is (256-LEVEL_CV) / 2 movwf OFFSET_HI clrf OFFSET_LO bcf CARRY rrf OFFSET_HI, f rrf OFFSET_LO, f ; Add the low byte movf OFFSET_LO, w addwf OUTPUT_LO, f btfsc CARRY incf OUTPUT_HI, f ; Add the high byte movf OFFSET_HI, w addwf OUTPUT_HI, f ; Set PWM duty cycle ;--------------------------------------- ; This puts the value of OUTPUT_HI, OUTPUT_LO into the appropriate ; registers. ; Note: we can set the duty cycle in registers ; CCP1CON and CCPR1L because they are double ; buffered and the changes will not take affect ; until the next PWM period starts (TMR2 resets). PWMOutput ; Put the 2 MSBs of OUTPUT_LO into CCP1CON ; bcf CARRY - unnecessary! I throw the low bits away! rlf OUTPUT_LO, w ; rotate bit 7 into the carry bit bsf CCP1CON, DC1B1 ; set or clear bit 5 of the CCP1CON register btfss CARRY bcf CCP1CON, DC1B1 ; bcf CARRY rlf OUTPUT_LO, w ; rotate bit 6 into the carry bit bsf CCP1CON, DC1B0 ; set or clear bit 4 of the CCP1CON register btfss CARRY bcf CCP1CON, DC1B0 ; Put the high byte into CCPR1L movf OUTPUT_HI, w movwf CCPR1L ;---------------------------------------- InterruptExit movfw FSR_TEMP ; restore FSR register movwf FSR movfw PCLATH_TEMP ; restore PCLATH register movwf PCLATH swapf STATUS_TEMP, w ; swap status_temp into W, sets bank to original state movwf STATUS ; restore STATUS register swapf W_TEMP, f swapf W_TEMP, w ; restore W register retfie ;---------------------------------------- ; Analogue to Digital conversion subroutine ; This is used by the main code loop ;---------------------------------------- DoADConversion ; Short delay whilst the channel settles movlw D'6' ; At 4 MHz, a 22 us delay movwf TEMP ; (22us = 2us + 6 * 3us + 1us) decfsz TEMP, f goto $-1 ; Start the conversion bsf ADCON0, GO ; Wait for it to finish btfsc ADCON0, GO ; Is it done? goto $ - 1 ; Read the ADC Value and store it movf ADRESH, w movwf ADC_VALUE return ;---------------------------------------- ; The main program ; This reads the A/D channels and provides ; values for the DDS ;---------------------------------------- Main clrf PORTC movlw 7 ; Turn off Comparators movwf CMCON0 clrf TMR2 ; Using TMR2 as a PWM Generator movlw b'00000100' ; Enable TMR2 movwf T2CON clrf CCPR1L ; Nothing Moving (Yet) bsf STATUS, RP0 ; Bank 1 ; Peripheral Interrupt Enable Register (PIE1) (Section 2.2.2.4) movlw b'00000010' ; TMR2 Overflow Interrupt: ENABLED movwf PIE1 ^ 0x80 movlw b'11110111' ; ADC Input on all except AN3 movwf ANSEL ^ 0x80 movlw b'00100000' ; Select ADC Clock as Fosc/32 movwf ADCON1 ^ 0x80 ; to ensure TAD of 1.6uS movlw D'255' ; Setup the Limit for the PWM movwf PR2 ^ 0x80 ; Maximum 19.5 KHz Frequency movlw b'001111' ; RC5:RC4 Outputs, RC3:RC0 Inputs movwf TRISC ^ 0x80 movlw b'111111' ; All inputs movwf TRISA ^ 0x80 movlw b'00001000' movwf IOCA ^ 0x80 ; Interrupt-on-change on RA3 bcf STATUS, RP0 ; Bank 0 ; Interrupt Control Register (INTCON) (Section 2.2.2.3) movlw b'00000000' ; Clear all peripheral interrupts movwf PIR1 movlw b'11001000' ; Peripheral Interrupts: ENABLED movwf INTCON ; (EEI, ADI, CCP1I, C2I, C1I, OSFI, TMR2I, TMR1I) ; PORT A Interrupt-on-change: ENABLED movlw b'00001100' ; Enable single channel PWM movwf CCP1CON ; Set up initial values of the variables movlw D'255' movwf LEVEL_CV ; Default to max output movlw D'128' movwf DISTORT_CV ; Default to 50% Square Wave clrf WAVE ; 0-Ramp Up 1-Ramp Down, 2-Pulse, 3-Triangle ; 4-Sine, 5-Sweep, 6-Spike, 7-Noise movlw D'1' movwf STEP_LO ; STEP (the S&H counter) counts down, not up movwf STEP_HI clrf STEP_RATE_HI clrf STEP_RATE_LO clrf STEP_MULT clrf FLAGS ; S&H and NOISE off to start with. clrf SYNC_IN_MODE ; Default is disabled comf SYNC_IN_MODE, f ; TESTING clrf SYNC_OUT_FREQ ; Default is LFO Freq clrf SHIFT_REG0 ;Might as well start with a seed of 1.6E7. clrf SHIFT_REG1 clrf SHIFT_REG2 clrf SHIFT_REG3 incf SHIFT_REG3, f clrf PHASE_LO clrf PHASE_MID clrf PHASE_HI clrf FREQ_INC_HI movlw D'56' ; Freq_Inc=56, 0.05Hz theorectically movwf FREQ_INC_LO clrf FREQ_INC_A_HI ; Clear the two actual freq incs clrf FREQ_INC_A_MID clrf FREQ_INC_A_LO clrf FREQ_INC_B_HI clrf FREQ_INC_B_MID clrf FREQ_INC_B_LO clrf ADC_CHANNEL ; Start with FREQ_CV MainLoop: ; Change to next A/D channel incf ADC_CHANNEL, f ; We need to do different things depending on which value we're reading: ; LFO Freq - Use value as an index to look up FREQ_INC from the table ; Waveform - Take only the top 3 bits. Store this as WAVE.Set/Clear NOISE_ENABLE as necessary ; Step Rate - Get the STEP RATE.Set/Clear STEP_ENABLE as necessary ; Output level - Store it as LEVEL_CV for the multiply routine ; Phase Distortion CV - Store it as DISTORT_CV for the UpdateFreqIncs routine ; Sync In Mode - Take top two bits, and store it ; Sync Out Freq - Take top two bits, and store it SelectADCChannel movlw HIGH ChannelOptions movwf PCLATH movf ADC_CHANNEL, w ; Get current channel andlw D'7' ; Only need three LSBs addlw LOW ChannelOptions btfsc CARRY incf PCLATH, f movwf PCL ChannelOptions goto FrequencyCV goto Waveform goto StepRateCV goto OutputLevelCV goto PhaseDistortionCV goto SyncInMode ; goto SyncOutFreq ScannedAllChannels ; If we jump to here, ADC_CHANNEL is 7 goto MainLoop goto MainLoop ; Update the Frequency CV FrequencyCV movlw b'00000001' ; AN0, ADC On movwf ADCON0 call DoADConversion ; Returns value in ADC_VALUE call GetFreqIncHi ; Uses index direct from ADC_VALUE, returns value in W movwf FREQ_INC_HI call GetFreqIncLo ; Uses index direct from ADC_VALUE, returns value in W movwf FREQ_INC_LO ; Update the two actual frequency increments then go to MainLoop goto UpdateFreqIncs ; Update the Waveform Waveform movlw b'00000101' ; AN1, ADC On movwf ADCON0 call DoADConversion ; Apply hysteresis to the CV by checking if ADC_VALUE and ADC_VALUE+4 give the same result addlw D'4' ; Add 4 to the AD value in W andlw B'11100000' ; Get the top 3 bits (Result 1) movwf TEMP movf ADC_VALUE, w andlw B'11100000' ; Get the top 3 bits (Result 2) xorwf TEMP, w ; Test if the two results are the same btfss ZEROBIT ; If zero, they're the same goto MainLoop ; Not the same, no need to change waveform ; We need to change the waveform bcf CARRY swapf ADC_VALUE, f movlw B'00001110' andwf ADC_VALUE, f ; We only need the top 3 bits rrf ADC_VALUE, w movwf WAVE ; Is the Noise Source enabled? xorlw D'7' ; Is this the NOISE wave? bcf NOISE_ENABLE ; Assume it isn't btfsc ZEROBIT ; If result is zero, Noise is on bsf NOISE_ENABLE ; NB: Although this method of checking could introduce a 2 cycle period when the NOISE ; isn't switched on when it should be, no-on will notice the glitch in the NOISE waveform. ; If the assumption was the other way around, it might have been a problem, with a tiny ; glitches of noise in a smooth waveform. goto MainLoop ; Update the Step (S&H) Rate CV, or turn the feature off StepRateCV movlw b'00001001' ; AN2, ADC On movwf ADCON0 call DoADConversion call GetStepRateHi ; Uses index direct from ADC_VALUE, returns value in W movwf STEP_RATE_HI call GetStepRateLo ; Uses index direct from ADC_VALUE, returns value in W movwf STEP_RATE_LO ; Is the S&H feature enabled? movf ADC_VALUE, w btfsc STEP_ENABLE ; Is it on currently? goto StepIsOn StepIsOff ; Check whether we should switch STEP_ENABLE on andlw B'11111000' ; Zero means result 7 or less - feature stays off btfss ZEROBIT ; If zero bit set, feature is off, so skip bsf STEP_ENABLE ; Turn S&H on if CV is 8 or above goto MainLoop StepIsOn ; Check whether we should switch STEP_ENABLE off andlw B'11111100' ; Zero means result 4 or less - feature goes off btfsc ZEROBIT ; If zero bit clear, feature is on, so skip bcf STEP_ENABLE ; Turn S&H off if CV is 4 or below goto MainLoop ; NB: This way isn't as efficient as the assumption method, but this way is glitch-free. ; Also note that the different turn on/turn off values give some hysteresis. ; Update the Output Level CV OutputLevelCV movlw b'00010001' ; AN4, ADC On movwf ADCON0 call DoADConversion movwf LEVEL_CV ; Simply store this one- easy! goto MainLoop ; Update the Phase Distortion CV PhaseDistortionCV movlw b'00010101' ; AN5, ADC On movwf ADCON0 call DoADConversion ; Limit the range of the DISTORT_CV PDCVTooLow ; Is it less than 4? andlw B'11111100' btfss ZEROBIT ; Is the ADC value 3 or less? goto PDCVTooHigh ; No, so move on movlw D'4' ; Yes, so set it to 4 minimum goto StorePDCV PDCVTooHigh ; Is it more than 251? xorlw B'11111111' ; Invert it andlw B'11111100' btfsc ZEROBIT ; Is the inverted value 3 or less? movlw D'3' ; Yes, so set it to 252 maximum xorlw B'11111111' ; Re-invert it to get it back StorePDCV movwf DISTORT_CV ; Store the phase distortion value ; Update the frequency increments then return to mainloop goto UpdateFreqIncs ; Update the Sync Input Mode SyncInMode movlw b'00011001' ; AN6, ADC On movwf ADCON0 call DoADConversion movwf SYNC_IN_MODE ; Store the 2 MSBs of ADC VALUE (We'll ignore the 6 LSBs) goto MainLoop ; Update the Sync Output Frequency SyncOutFreq movlw b'00011101' ; AN7, ADC On movwf ADCON0 call DoADConversion movwf SYNC_OUT_FREQ ; Store the 2 MSBs of ADC VALUE (We'll ignore the 6 LSBs) goto MainLoop ;---------------------------------------- ; Update frequency increments (A+B) subroutine ; This is used by FrequencyCV and ; PhaseDistortionCV in the main code loop ;---------------------------------------- UpdateFreqIncs ; Get the FREQ INC value and multiply it by 128. Store in INCTEMP. clrf INCTEMP_LO bcf CARRY rrf FREQ_INC_HI, w movwf INCTEMP_HI rrf FREQ_INC_LO, w movwf INCTEMP_MID rrf INCTEMP_LO, f ; Shift the extra bit into the low byte ; Divide by the DISTORT_CV value movf DISTORT_CV, w ; Put the DISTORT_CV value into the divisor movwf DIVISOR call Division ; Do the division ; Store result as INC A movf NUMBER_HI, w movwf FREQ_INC_A_HI movf NUMBER_MID, w movwf FREQ_INC_A_MID movf NUMBER_LO, w movwf FREQ_INC_A_LO ; Divide by 256-DISTORT_CV value comf DISTORT_CV, w ; Put 256-DISTORT_CV value into the divisor movwf DIVISOR call Division ; Do the division ; Store result as INC B movf NUMBER_HI, w movwf FREQ_INC_B_HI movf NUMBER_MID, w movwf FREQ_INC_B_MID movf NUMBER_LO, w movwf FREQ_INC_B_LO ; And that's the lot! goto MainLoop ;*********************************************************** ; Unsigned 24 bit by 8 bit divide routine ; ; Inputs: ; Dividend - NUMBER_HI,NUMBER_MID,NUMBER_LO ; Divisor - DIVISOR ; Temporary: ; Counter - COUNTER ; Output: ; Quotient - NUMBER_HI,NUMBER_MID,NUMBER_LO ; Remainder - REMAIN ; ; Size: 17 ; Timing: 342 cycles (including call and return) ; ; This is basically Nikolai Golovchenko's 24 by 16 bit ; divide routine, with some instructions removed to ; optimise it for an 8 bit divide. ; Thanks to Nikolai for the original post. ; ; James Hillman, 2 December 2005 ;*********************************************************** Division clrf REMAIN ; Clear remainder movlw D'24' movwf DIVTEMP GetNumerator ; Put INCTEMP (raw freq inc x 128) into the numerator movf INCTEMP_HI, w movwf NUMBER_HI movf INCTEMP_MID, w movwf NUMBER_MID movf INCTEMP_LO, w movwf NUMBER_LO DivisionLoop rlf NUMBER_LO, w ; Shift dividend left to move next bit to remainder rlf NUMBER_MID, f rlf NUMBER_HI, f rlf REMAIN, f ; Shift carry (next dividend bit) into remainder rlf NUMBER_LO, f ; Finish shifting the dividend and save carry in NUMBER_LO.0, ; since REMAIN can be 9 bit long in some cases ; This bit will also serve as the next result bit. movf DIVISOR, w ; Subtract divisor from 8-bit REMAIN subwf REMAIN, f ;here we also need to take into account the 9th bit of REMAIN, which ;is in NUMBER_LO.0. If we don't have a borrow after subtracting from ;8 bits of REMAIN, then there is no borrow regardless of 9th bit ;value. But, if we have the borrow, then that will depend on 9th bit ;value. If it is 1, then no final borrow will occur. If it is 0, borrow ;will occur. These values match the borrow flag polarity. btfsc BORROW ;if no borrow after 8 bit subtraction bsf NUMBER_LO, 0 ;then there is no borrow in result. Overwrite ;NUMBER_LO.0 with 1 to indicate no borrow. ;if borrow did occur, NUMBER_LO.0 already ;holds the final borrow value (0-borrow, ;1-no borrow) btfss NUMBER_LO, 0 ;if no borrow after 9-bit subtraction addwf REMAIN, f ;restore REMAIN. (w contains the value ;subtracted from it previously) decfsz DIVTEMP, f goto DivisionLoop return ;---------------------------------------------- ; Waveform Lookup Tables ; In actual fact, the straight-line waveforms ; are calculated rather than looked-up. ; It's faster that way. ;---------------------------------------------- ; Ramp Up RampUp movf PHASE_HI, w ; Phase accumulator high byte goto ReturnWithValue ; Ramp Down RampDown comf PHASE_HI, w ; Invert Phase accumulator high byte goto ReturnWithValue ; Pulse waveform Pulse ;Is PHASE_HI higher than PWM CV? ; movf PHASE_HI, w ; Phase accumulator high byte ; subwf DISTORT_CV, w ; btfsc BORROW ; Are we on the second half? btfsc PHASE_HI, 7 goto PulseHigh PulseLow movlw D'0' ; Low value goto ReturnWithValue PulseHigh movlw D'255' ; High value goto ReturnWithValue ; Triangle Triangle bcf CARRY rlf PHASE_MID, w rlf PHASE_HI, w ; Phase accumulator high byte x 2 ;Is PHASE_HI higher than 127? btfsc PHASE_HI, 7 xorlw D'255' ; Invert it for falling part of wave goto ReturnWithValue ; Sine Sine bcf CARRY rlf PHASE_MID, w rlf PHASE_HI, w ; Phase accumulator high byte x 2 call GetHalfSine ; Lookup sine table movwf WAVETEMP ; Store lookup value in temp storage bcf CARRY rrf WAVETEMP, f ; Divide lookup value by 2 bsf WAVETEMP, 7 ; Add 128 to lookup alue movf WAVETEMP, w btfsc PHASE_HI, 7 ; If PHASE in second half xorlw D'255' ; then invert it goto ReturnWithValue ; Log Sweep Sweep movf PHASE_HI, w call GetHalfSine ; Lookup sine table xorlw D'255' ; Invert it movwf WAVETEMP goto ReturnWithValue ; Spike ; In order to boost the resolution, the spike wave is divided into quarters ; Two quarters are flat, and the others are mapped to the table. Spike btfsc PHASE_HI, 6 ; If in 1st or 3rd quarter goto SpikeFlat SpikeRequired movf PHASE_MID, w movwf WAVETEMP movf PHASE_HI, w movwf WAVETEMP2 bcf CARRY rlf WAVETEMP, f rlf WAVETEMP2, f rlf WAVETEMP, f rlf WAVETEMP2, w ; Phase accumulator high byte x 4 call GetHalfSpike ; Lookup transient spike table btfsc PHASE_HI, 7 ; If in 1st quarter, skip inversion xorlw D'255' ; Invert the spike goto ReturnWithValue SpikeFlat ; 2nd or 4th quarter movlw D'128' ; All quiet on the western front... goto ReturnWithValue ; Return a psuedo-random byte from the noise source Noise movf SHIFT_REG3, w goto ReturnWithValue ;--------------------------------------------- ; Waveform Lookup Tables ; There are two. One contains half a sine wave ; used for the SINE and SWEEP waves. The other ; is a transient spike,used for the SPIKE wave ;---------------------------------------------- GetHalfSine movwf WAVETEMP movlw HIGH SineTable movwf PCLATH movfw WAVETEMP addlw LOW SineTable btfsc CARRY incf PCLATH, f movwf PCL SineTable dt D'0', D'3', D'6', D'9', D'13', D'16', D'19', D'22' dt D'25', D'28', D'31', D'34', D'37', D'41', D'44', D'47' dt D'50', D'53', D'56', D'59', D'62', D'65', D'68', D'71' dt D'74', D'77', D'80', D'83', D'86', D'89', D'92', D'95' dt D'98', D'100', D'103', D'106', D'109', D'112', D'115', D'117' dt D'120', D'123', D'126', D'128', D'131', D'134', D'136', D'139' dt D'142', D'144', D'147', D'149', D'152', D'154', D'157', D'159' dt D'162', D'164', D'167', D'169', D'171', D'174', D'176', D'178' dt D'180', D'183', D'185', D'187', D'189', D'191', D'193', D'195' dt D'197', D'199', D'201', D'203', D'205', D'207', D'209', D'210' dt D'212', D'214', D'215', D'217', D'219', D'220', D'222', D'223' dt D'225', D'226', D'228', D'229', D'231', D'232', D'233', D'234' dt D'236', D'237', D'238', D'239', D'240', D'241', D'242', D'243' dt D'244', D'245', D'246', D'247', D'247', D'248', D'249', D'249' dt D'250', D'251', D'251', D'252', D'252', D'253', D'253', D'253' dt D'254', D'254', D'254', D'255', D'255', D'255', D'255', D'255' dt D'255', D'255', D'255', D'255', D'255', D'255', D'254', D'254' dt D'254', D'253', D'253', D'253', D'252', D'252', D'251', D'251' dt D'250', D'249', D'249', D'248', D'247', D'247', D'246', D'245' dt D'244', D'243', D'242', D'241', D'240', D'239', D'238', D'237' dt D'236', D'234', D'233', D'232', D'230', D'229', D'228', D'226' dt D'225', D'223', D'222', D'220', D'219', D'217', D'215', D'214' dt D'212', D'210', D'208', D'207', D'205', D'203', D'201', D'199' dt D'197', D'195', D'193', D'191', D'189', D'187', D'185', D'182' dt D'180', D'178', D'176', D'173', D'171', D'169', D'166', D'164' dt D'162', D'159', D'157', D'154', D'152', D'149', D'147', D'144' dt D'142', D'139', D'136', D'134', D'131', D'128', D'126', D'123' dt D'120', D'117', D'115', D'112', D'109', D'106', D'103', D'100' dt D'98', D'95', D'92', D'89', D'86', D'83', D'80', D'77' dt D'74', D'71', D'68', D'65', D'62', D'59', D'56', D'53' dt D'50', D'47', D'43', D'40', D'37', D'34', D'31', D'28' dt D'25', D'22', D'19', D'16', D'12', D'9', D'6', D'3' GetHalfSpike movwf WAVETEMP movlw HIGH SpikeTable movwf PCLATH movfw WAVETEMP addlw LOW SpikeTable btfsc CARRY incf PCLATH, f movwf PCL SpikeTable dt D'129', D'129', D'129', D'129', D'129', D'129', D'129', D'129' dt D'129', D'129', D'129', D'129', D'129', D'129', D'129', D'129' dt D'129', D'130', D'130', D'130', D'130', D'130', D'130', D'130' dt D'130', D'130', D'130', D'131', D'131', D'131', D'131', D'131' dt D'131', D'132', D'132', D'132', D'132', D'132', D'132', D'133' dt D'133', D'133', D'133', D'134', D'134', D'134', D'134', D'135' dt D'135', D'135', D'135', D'136', D'136', D'137', D'137', D'138' dt D'138', D'139', D'139', D'140', D'140', D'141', D'141', D'142' dt D'142', D'143', D'143', D'144', D'144', D'145', D'145', D'146' dt D'147', D'148', D'148', D'149', D'150', D'151', D'152', D'153' dt D'154', D'155', D'156', D'157', D'158', D'159', D'160', D'162' dt D'163', D'165', D'166', D'168', D'169', D'171', D'172', D'174' dt D'175', D'177', D'178', D'180', D'182', D'184', D'186', D'188' dt D'190', D'192', D'194', D'197', D'199', D'202', D'204', D'207' dt D'209', D'212', D'215', D'218', D'221', D'224', D'227', D'231' dt D'234', D'238', D'241', D'245', D'248', D'251', D'253', D'255' dt D'253', D'251', D'248', D'245', D'241', D'238', D'234', D'231' dt D'227', D'224', D'221', D'218', D'215', D'212', D'209', D'207' dt D'204', D'202', D'199', D'197', D'194', D'192', D'190', D'188' dt D'186', D'184', D'182', D'180', D'178', D'177', D'175', D'174' dt D'172', D'171', D'169', D'168', D'166', D'165', D'163', D'162' dt D'160', D'159', D'158', D'157', D'156', D'155', D'154', D'153' dt D'152', D'151', D'150', D'149', D'148', D'148', D'147', D'146' dt D'145', D'145', D'144', D'144', D'143', D'143', D'142', D'142' dt D'141', D'141', D'140', D'140', D'139', D'139', D'138', D'138' dt D'137', D'137', D'136', D'136', D'135', D'135', D'135', D'135' dt D'134', D'134', D'134', D'134', D'133', D'133', D'133', D'133' dt D'132', D'132', D'132', D'132', D'132', D'132', D'131', D'131' dt D'131', D'131', D'131', D'131', D'130', D'130', D'130', D'130' dt D'130', D'130', D'130', D'130', D'130', D'130', D'129', D'129' dt D'129', D'129', D'129', D'129', D'129', D'129', D'129', D'129' dt D'129', D'129', D'129', D'129', D'129', D'129', D'129', D'129' ;----------------------------------------- ; Frequency Control Lookup Table ; Converts from 0-255 CV input to 16 bit ; FREQ_INC value ; The main purpose of this is to provide ; a logarithmic (equal octave) response ; to the LFO FREQ control ; The tables should be called with the ; index in ADC_VALUE, (NOT in the W register), ; and will return the required value ;----------------------------------------- GetFreqIncHi movlw HIGH FreqLookupHi movwf PCLATH movf ADC_VALUE, w addlw LOW FreqLookupHi btfsc CARRY incf PCLATH, f movwf PCL FreqLookupHi dt 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 dt 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 dt 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 dt 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 dt 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 dt 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 dt 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 dt 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2 dt 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3 dt 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5 dt 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x7, 0x7, 0x7 dt 0x7, 0x7, 0x7, 0x8, 0x8, 0x8, 0x8, 0x8, 0x9, 0x9, 0x9, 0x9, 0x9, 0xa, 0xa, 0xa dt 0xa, 0xa, 0xb, 0xb, 0xb, 0xb, 0xc, 0xc, 0xc, 0xd, 0xd, 0xd, 0xd, 0xe, 0xe, 0xe dt 0xf, 0xf, 0xf, 0x10, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x14, 0x14, 0x15 dt 0x15, 0x15, 0x16, 0x16, 0x17, 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d dt 0x1e, 0x1f, 0x1f, 0x20, 0x21, 0x21, 0x22, 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a GetFreqIncLo movlw HIGH FreqLookupLo movwf PCLATH movf ADC_VALUE, w ; Get index directly from ADC_VALUE addlw LOW FreqLookupLo btfsc CARRY incf PCLATH, f movwf PCL FreqLookupLo dt 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3c dt 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x44, 0x45, 0x47, 0x48, 0x4a, 0x4c, 0x4d, 0x4f, 0x51, 0x52, 0x54 dt 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64, 0x66, 0x69, 0x6b, 0x6d, 0x70, 0x72, 0x75, 0x77 dt 0x7a, 0x7c, 0x7f, 0x82, 0x85, 0x88, 0x8b, 0x8e, 0x91, 0x94, 0x97, 0x9a, 0x9e, 0xa1, 0xa5, 0xa8 dt 0xac, 0xb0, 0xb4, 0xb8, 0xbc, 0xc0, 0xc4, 0xc8, 0xcd, 0xd1, 0xd6, 0xda, 0xdf, 0xe4, 0xe9, 0xee dt 0xf3, 0xf9, 0xfe, 0x4, 0x9, 0xf, 0x15, 0x1b, 0x21, 0x28, 0x2e, 0x35, 0x3c, 0x42, 0x4a, 0x51 dt 0x58, 0x60, 0x67, 0x6f, 0x77, 0x80, 0x88, 0x90, 0x99, 0xa2, 0xab, 0xb5, 0xbe, 0xc8, 0xd2, 0xdc dt 0xe7, 0xf1, 0xfc, 0x7, 0x13, 0x1e, 0x2a, 0x36, 0x43, 0x4f, 0x5c, 0x6a, 0x77, 0x85, 0x93, 0xa2 dt 0xb0, 0xbf, 0xcf, 0xdf, 0xef, 0xff, 0x10, 0x21, 0x33, 0x44, 0x57, 0x69, 0x7d, 0x90, 0xa4, 0xb9 dt 0xcd, 0xe3, 0xf8, 0xf, 0x25, 0x3d, 0x54, 0x6d, 0x86, 0x9f, 0xb9, 0xd3, 0xee, 0xa, 0x26, 0x43 dt 0x61, 0x7f, 0x9e, 0xbd, 0xdd, 0xfe, 0x20, 0x42, 0x65, 0x89, 0xae, 0xd3, 0xf9, 0x20, 0x48, 0x71 dt 0x9b, 0xc5, 0xf1, 0x1e, 0x4b, 0x79, 0xa9, 0xda, 0xb, 0x3e, 0x72, 0xa7, 0xdd, 0x14, 0x4c, 0x86 dt 0xc1, 0xfd, 0x3b, 0x7a, 0xba, 0xfc, 0x3f, 0x84, 0xca, 0x12, 0x5b, 0xa6, 0xf2, 0x41, 0x91, 0xe2 dt 0x36, 0x8b, 0xe2, 0x3b, 0x96, 0xf3, 0x52, 0xb3, 0x16, 0x7c, 0xe3, 0x4d, 0xb9, 0x28, 0x99, 0xc dt 0x82, 0xfb, 0x76, 0xf4, 0x75, 0xf8, 0x7f, 0x8, 0x94, 0x24, 0xb6, 0x4c, 0xe5, 0x81, 0x21, 0xc4 dt 0x6b, 0x16, 0xc4, 0x76, 0x2c, 0xe6, 0xa4, 0x66, 0x2d, 0xf7, 0xc7, 0x9a, 0x73, 0x50, 0x32, 0x19 ;----------------------------------------- ; Step Rate(S&H) Control Lookup Table ; Converts from 0-255 CV input to 16 bit ; STEP_RATE value ; The main purpose of this is to provide ; a logarithmic (equal octave) response ; to the STEP_RATE control ; The tables should be called with the ; index in ADC_VALUE, (NOT in the W register), ; and will return the required value ;----------------------------------------- GetStepRateHi movlw HIGH StepLookupHi movwf PCLATH movf ADC_VALUE, w addlw LOW StepLookupHi btfsc CARRY incf PCLATH, f movwf PCL StepLookupHi dt 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23 dt 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1f, 0x1e, 0x1d, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, 0x1a, 0x19, 0x19 dt 0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x12 dt 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0xf, 0xf, 0xf, 0xe, 0xe, 0xe, 0xd, 0xd, 0xd, 0xd dt 0xc, 0xc, 0xc, 0xc, 0xb, 0xb, 0xb, 0xb, 0xb, 0xa, 0xa, 0xa, 0xa, 0x9, 0x9, 0x9 dt 0x9, 0x9, 0x9, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7 dt 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5 dt 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4 dt 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3 dt 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2 dt 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2 dt 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 dt 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 dt 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 dt 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 dt 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 GetStepRateLo movlw HIGH StepLookupLo movwf PCLATH movf ADC_VALUE, w ; Get index directly from ADC_VALUE addlw LOW StepLookupLo btfsc CARRY incf PCLATH, f movwf PCL StepLookupLo dt 0x9c, 0x97, 0x97, 0x9d, 0xa8, 0xb9, 0xcf, 0xe9, 0x9, 0x2d, 0x56, 0x84, 0xb6, 0xed, 0x28, 0x67 dt 0xaa, 0xf2, 0x3d, 0x8c, 0xdf, 0x36, 0x90, 0xee, 0x4f, 0xb4, 0x1c, 0x87, 0xf6, 0x67, 0xdc, 0x54 dt 0xce, 0x4c, 0xcc, 0x4f, 0xd4, 0x5d, 0xe8, 0x75, 0x5, 0x97, 0x2b, 0xc2, 0x5b, 0xf7, 0x94, 0x34 dt 0xd5, 0x79, 0x1f, 0xc6, 0x70, 0x1b, 0xc8, 0x77, 0x28, 0xda, 0x8e, 0x44, 0xfb, 0xb4, 0x6e, 0x2a dt 0xe7, 0xa6, 0x66, 0x28, 0xea, 0xaf, 0x74, 0x3b, 0x3, 0xcc, 0x96, 0x61, 0x2e, 0xfc, 0xca, 0x9a dt 0x6b, 0x3d, 0x10, 0xe3, 0xb8, 0x8e, 0x64, 0x3c, 0x14, 0xed, 0xc7, 0xa2, 0x7e, 0x5a, 0x37, 0x15 dt 0xf4, 0xd3, 0xb3, 0x94, 0x75, 0x58, 0x3a, 0x1e, 0x2, 0xe6, 0xcb, 0xb1, 0x97, 0x7e, 0x65, 0x4d dt 0x36, 0x1f, 0x8, 0xf2, 0xdc, 0xc7, 0xb2, 0x9e, 0x8a, 0x77, 0x64, 0x51, 0x3f, 0x2d, 0x1c, 0xb dt 0xfa, 0xea, 0xda, 0xca, 0xbb, 0xac, 0x9d, 0x8f, 0x81, 0x73, 0x66, 0x59, 0x4c, 0x3f, 0x33, 0x27 dt 0x1b, 0x10, 0x4, 0xf9, 0xee, 0xe4, 0xd9, 0xcf, 0xc5, 0xbc, 0xb2, 0xa9, 0xa0, 0x97, 0x8e, 0x86 dt 0x7d, 0x75, 0x6d, 0x65, 0x5e, 0x56, 0x4f, 0x48, 0x41, 0x3a, 0x33, 0x2d, 0x26, 0x20, 0x1a, 0x14 dt 0xe, 0x8, 0x2, 0xfd, 0xf7, 0xf2, 0xed, 0xe8, 0xe3, 0xde, 0xd9, 0xd5, 0xd0, 0xcc, 0xc7, 0xc3 dt 0xbf, 0xbb, 0xb7, 0xb3, 0xaf, 0xab, 0xa8, 0xa4, 0xa1, 0x9d, 0x9a, 0x97, 0x93, 0x90, 0x8d, 0x8a dt 0x87, 0x84, 0x81, 0x7f, 0x7c, 0x79, 0x77, 0x74, 0x72, 0x6f, 0x6d, 0x6b, 0x68, 0x66, 0x64, 0x62 dt 0x60, 0x5e, 0x5c, 0x5a, 0x58, 0x56, 0x54, 0x52, 0x51, 0x4f, 0x4d, 0x4c, 0x4a, 0x48, 0x47, 0x45 dt 0x44, 0x42, 0x41, 0x40, 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x34, 0x33, 0x32, 0x31 ; We never reach here end