DDS with an envelope scaling
I have been up all night working with PASM, writing to main memory, using the serial debugger, and trying to work out what I need to do, however, there are just things that are happening that I really don't understand. So I'll post what I have and what is happening, and also what I'm trying to do, and hopefully one of you geniuses can enlighten my soupy brain. This one might be a challenge but I'll be active on it all day, so here we go.
What I'm trying to do:
I have built a 25-key keyboard with resistive pressure sensors that go into 4 8-channel adcs, and are read into an array in main memory by the mcp3208-driver, that works great. I want to create the frequency of the key at an amplitude proportional to the pressure the key being hit.
Alright, so I started with this code from kuroneko, and tdlivings in order to have a continuously updated sine wave frequency. Got this to work well.
. So I don't understand why this is. I have to be able to copy the value from the wavetable, and manipulate it in order to do the scaling operations, and then be able to put out the result. But before I do that I just want to create the sine wave from values copied from the wavetable so that I know I can reliably operate on the waveform. I only need to be able to produce frequencies up to about 2kHz, so I'm pretty sure this is doable. Here is the entire DAT file I'm working with, there is some debugging code commented out.
What I'm trying to do:
I have built a 25-key keyboard with resistive pressure sensors that go into 4 8-channel adcs, and are read into an array in main memory by the mcp3208-driver, that works great. I want to create the frequency of the key at an amplitude proportional to the pressure the key being hit.
Alright, so I started with this code from kuroneko, and tdlivings in order to have a continuously updated sine wave frequency. Got this to work well.
org 0
entry
'256 sampled 8-bit sine table. Replace with any sampled waveform you like
'as long as the data table is of the same dimension and depth. Only longs are
'used to make the DDS loop as short as possible in terms of clock cycles.
Sine long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE
long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8
long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5
long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF
long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7
long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC
long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3
long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83
long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51
long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27
long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A
long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00
long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08
long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23
long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C
long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C
mov dira, PinMsk 'LSB to PA0, LSB to PA7
:loop rdlong M_,par 'Get Freq Control Value
add Acc, M_ 'M $1FF max if literal with #$xxx
ror Acc, #16 'Postion index
movs :inline, Acc 'Store 9-bit index
andn :inline, #%1_00000000 'Clamp to 8-bits
rol Acc, #16 'Restore accumulator
:inline mov outa, 0-0 'Get long value from table
jmp #:loop 'Loop again
PinMsk long $FF '8-bit DAC, LSB on PA0, MSB on PA7
state long $100
M_ long 0 'Tuning word var init
Acc long 0 'Accumulator var init
AddrFreq long 0 'Address of Freq Control in Main Spin Code
Then, (trying to take baby steps) instead of movs of the Acc to :inline, I moved it to a local variable x so I could perform scaling operations on the value with:
movs :scale, Acc
:scale movs x, 0-0
movs :inline, x
I realize that I will have to do (probably pin) synchronization at the output with a counter since multiplication and division operation take variable amounts of time. I tried so many different ways to do this, including not even using dynamic source addressing and just going: mov outa, x....or using the literal for x in the movs: movs, :inline,#x.......but no acceptable output. Anytime I do get an output waveform, every other period is completely scrambled:
DAT
org 0
entry
'256 sampled 8-bit sine table. Replace with any sampled waveform you like
'as long as the data table is of the same dimension and depth. Only longs are
'used to make the DDS loop as short as possible in terms of clock cycles.
Sine long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE
long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8
long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5
long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF
long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7
long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC
long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3
long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83
long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51
long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27
long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A
long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00
long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08
long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23
long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C
long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C
{{mov dira, PinMsk 'LSB to PA0, LSB to PA7
mov fPtr, par
add t1, fPtr
add t1, #48
mov envPtr, t1
:loop rdlong M1_, fPtr 'Get Freq Control Value
add Acc1, M1_ wz 'M $1FF max if literal with #$xxx
ror Acc1, #16 'Postion index
movs :setMult, Acc1
rdlong x, envPtr
:setMult mov y, 0-0
call #multiply
mov y, #0100 'dividing must be done b/c no floating point
call #divide
movs :inline, x
andn :inline, #%1_00000000 'Clamp to 8-bits
rol Acc1, #16 'Restore accumulator
:inline mov outa, 0-0 'Get long value from table
jmp #:loop 'Loop again 'Loop again }}
mov dira, PinMsk 'LSB to PA0, LSB to PA7
:loop rdlong M_,par 'Get Freq Control Value
add Acc, M_ 'M $1FF max if literal with #$xxx
ror Acc, #16 'Postion index
' movs :inline, Acc 'Store 9-bit index
movs :scale, Acc
:scale movs x, 0-0
movs :inline, x
' mov y,#2
' call #divide
mov t1, par
add t1, #96
wrlong x, t1
andn :inline, #%1_00000000 'Clamp to 8-bits
rol Acc, #16 'Restore accumulator
:inline mov outa, 0-0 'Get long value from table
djnz count, #:loop
jmp #:loop 'Loop again
'ret
'' Multiply x[15..0] by y[15..0] (y[31..16] must be 0)
' on exit, product in y[31..0]
'
mult shl x,#16 'get multiplicand into x[31..16]
mov t1,#16 'ready for 16 multiplier bits
shr y,#1 wc 'get initial multiplier bit into c
:loop if_c add y,x wc 'if c set, add multiplicand to product
rcr y,#1 wc 'put next multiplier in c, shift prod.
djnz t1,#:loop 'loop until done
mult_ret ret 'return with product in y[31..0]
' Divide x[31..0] by y[15..0] (y[16] must be 0)
' on exit, quotient is in x[15..0] and remainder is in x[31..16]
divide shl y,#15 'get divisor into y[30..15]
mov t1,#16 'ready for 16 quotient bits
:loop cmpsub x,y wc 'y =< x? Subtract it, quotient bit in c
rcl x,#1 'rotate c into quotient, shift dividend
djnz t1,#:loop 'loop until done
divide_ret ret 'quotient in x[15..0]
PinMsk long $FF '8-bit DAC, LSB on PA0, MSB on PA7
t1 long 0
x long 0
y long 0
M_ long 0 'Tuning word var init
Acc long 0 'Accumulator var init
fPtr long 0 'Address of Freq Control in Main Spin
envPtr long 0
count long 256
Please help! I can't wait to get this working! 
Comments
movs :scale, Acc nop :scale movs x, 0-0 movs :inline, xmovs instructions in a row. The manual says you need at least one instruction between a movs and the
use of the target. The :scale sequence for sure looks like it needs a nop added between.
Tom
Thanks for responding guys....that makes sense. Tried it out and I still seem to be having this scrambling issue:
mov dira, PinMsk 'LSB to PA0, LSB to PA7 :loop rdlong M_,par 'Get Freq Control Value add Acc, M_ 'M $1FF max if literal with #$xxx ror Acc, #16 'Postion index ' movs :inline, Acc 'Store 9-bit index movs :scale, Acc nop :scale mov x, 0-0 movs :inline, x ' mov y,#2 ' call #divide 'mov t1, par 'add t1, #96 'wrlong count, t1 andn :inline, #%1_00000000 'Clamp to 8-bits rol Acc, #16 'Restore accumulator :inline mov outa, 0-0 'Get long value from table jmp #:loop 'Loop againI just can't think of what's causing it, it seems like an issue with Acc, since it's happening once every (what would be) 2 periods.Edit: Wait, no that's not the problem... let me see again... (I'm using a simulator
Edit2: Nonono, the problem is when you do the second movs because you allow x to be loaded with any value in the 0..511 range instead of 0.255 where your waveform resides... (the andn should be for the instruction at scale and not at inline as the waveform has values in the 0..255 range).
mov dira, PinMsk 'LSB to PA0, LSB to PA7 :loop rdlong M_,par 'Get Freq Control Value add Acc, M_ 'M $1FF max if literal with #$xxx ror Acc, #16 'Postion index movs :scale, Acc andn :scale, #%1_00000000 'Clamp to 8-bits nop :scale mov x, 0-0 ' mov y,#2 ' call #divide mov t1, par add t1, #96 wrlong x, t1 rol Acc, #16 'Restore accumulator ' waitpeq timerState, state 'waitpne timerState, state :inline mov outa, x 'Get long value from table jmp #:loop 'Loop againTHANKS ALE. I will keep this thread going throughout the day if I have any other major runins.Here is my code:
DAT org 0 entry '256 sampled 8-bit sine table. Replace with any sampled waveform you like 'as long as the data table is of the same dimension and depth. Only longs are 'used to make the DDS loop as short as possible in terms of clock cycles. Sine long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8 long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5 long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7 long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3 long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83 long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51 long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27 long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00 long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08 long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23 long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C {{mov dira, PinMsk 'LSB to PA0, LSB to PA7 mov fPtr, par add t1, fPtr add t1, #48 mov envPtr, t1 :loop rdlong M1_, fPtr 'Get Freq Control Value add Acc1, M1_ wz 'M $1FF max if literal with #$xxx ror Acc1, #16 'Postion index movs :setMult, Acc1 rdlong x, envPtr :setMult mov y, 0-0 call #multiply mov y, #0100 'dividing must be done b/c no floating point call #divide movs :inline, x andn :inline, #%1_00000000 'Clamp to 8-bits rol Acc1, #16 'Restore accumulator :inline mov outa, 0-0 'Get long value from table jmp #:loop 'Loop again 'Loop again }} mov t1,par add t1, 96 mov envPtr, t1 mov t1,par add t1, 24 mov pressPtr, t1 mov dira, PinMsk 'LSB to PA0, LSB to PA7 :loop rdlong M_,par 'Get Freq Control Value add Acc, M_ 'M $1FF max if literal with #$xxx ror Acc, #16 'Postion index rdlong y, pressPtr movs :val, Acc andn :val, #%1_00000000 'Clamp to 8-bits rol Acc, #16 'Restore accumulator :val mov x, 0-0 mov t2,x 'store sine value for subtraction after factoring shl x,#2 'shift left for multiplication and division call #mult 'multiply 'wrlong y,envPtr 'ret mov x,y 'move mult output into x mov y,pScale 'setup y to divide x by 4095 call #divide 'do it shr x, #2 'shift it back over andn x, #%1_00000000 'Clamp to 8-bits waitpeq timerState, state 'to evenly space loops in time waitpne timerState, state ' ^^same :inline mov outa, x 'move value out jmp #:loop 'Loop again 'ret '' Multiply x[15..0] by y[15..0] (y[31..16] must be 0) ' on exit, product in y[31..0] ' mult shl x,#16 'get multiplicand into x[31..16] mov t1,#16 'ready for 16 multiplier bits shr y,#1 wc 'get initial multiplier bit into c :loop if_c add y,x wc 'if c set, add multiplicand to product rcr y,#1 wc 'put next multiplier in c, shift prod. djnz t1,#:loop 'loop until done mult_ret ret 'return with product in y[31..0] ' Divide x[31..0] by y[15..0] (y[16] must be 0) ' on exit, quotient is in x[15..0] and remainder is in x[31..16] divide shl y,#15 'get divisor into y[30..15] mov t1,#16 'ready for 16 quotient bits :loop cmpsub x,y wc 'y =< x? Subtract it, quotient bit in c rcl x,#1 'rotate c into quotient, shift dividend djnz t1,#:loop 'loop until done divide_ret ret 'quotient in x[15..0] PinMsk long $FF '8-bit DAC, LSB on PA0, MSB on PA7 timerState long $100 state long $100 pScale long 4095 t1 long 0 t2 long 0 x long 0 y long 0 M_ long 0 'Tuning word var init Acc long 0 'Accumulator var init fPtr long 0 'Address of Freq Control in Main Spin pressPtr long 0 envPtr long 0 envState long 0I'm only using the envPtr to report values back to the hub for serial debugging.I'm trying to do this:
SineValue = (SineValue - (pressVal/4095)*SineValue)
What you do wrong is: You multiply the index into the sine table and not the value read from the sine table.
There are other issues in your code, so I took the initial Sine DDS code and added the modulation of the amplitude by my own. I think this makes it more clear than modifying your last posted code.
This is as simple as possible, but not perfect. It calculates:
SineValue = pressVal * SineValue / 4096
If you want the loudest tone if presVal is 0 then calculate it like that: presVal := 4095 - ADCvalue
Instead of a division by 4095 you can divide by 4096 and that is a simple right shift by 12.
The multiplication is not optimized, you need only a 8bit * 16bit mult which then would be two times faster.
And the zero point of the output signal is not in the middle (value 128) instead the signal goes to value 0 with the EnvValue=0. To make it right you need to calculate:
SineValue = (SineValue - 128) * pressVal / 4096 + 128
with the used sine table, but for that you need a signed multiplication, which makes it more complicated.
Here is the code:
VAR long FreqVal, EnvVal ' ^par DAT org 0 entry '256 sampled 8-bit sine table. Replace with any sampled waveform you like 'as long as the data table is of the same dimension and depth. Only longs are 'used to make the DDS loop as short as possible in terms of clock cycles. Sine long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8 long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5 long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7 long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3 long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83 long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51 long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27 long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00 long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08 long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23 long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C mov dira, PinMsk 'LSB to PA0, LSB to PA7 mov EnvPtr, par add EnvPtr, #4 'calc EnvAddr :loop rdlong frq,par 'Get Freq Control Value add Acc, frq 'DDS accumulate mov t1, Acc shr t1, #24 'use highest 8 bits as pointer movs :inline, t1 ' into sine table nop :inline mov x, 0-0 'Get value from table (8 bit) rdlong y, EnvPtr 'Get Env value (12 bit) call #mult 'sineVal * env shr y, #12 'divide by 4096 mov outa, y 'write to DAC at P0..P7 jmp #:loop 'Loop again '' Multiply x[15..0] by y[15..0] (y[31..16] must be 0) ' on exit, product in y[31..0] ' mult shl x,#16 'get multiplicand into x[31..16] mov t1,#16 'ready for 16 multiplier bits shr y,#1 wc 'get initial multiplier bit into c :loop if_c add y,x wc 'if c set, add multiplicand to product rcr y,#1 wc 'put next multiplier in c, shift prod. djnz t1,#:loop 'loop until done mult_ret ret 'return with product in y[31..0] PinMsk long $FF '8-bit DAC, LSB on PA0, MSB on PA7 state long $100 frq long 0 'Tuning word var init Acc long 0 'Accumulator var init AddrFreq long 0 'Address of Freq Control in Main Spin Code EnvPtr long 0 'Address of EnvValue (12bit) x long 0 y long 0 t1 long 0Andy
I see now that the "other issues" I mentoined are in a commented out section of the code, so I think the above is the main error.
Andy
I have all the frequencies and envelopes in being set in a 6-long array in main memory. For now I'm just trying to mix 2 signals (the rest is commented out). I tried setting z, and checking for that when outputting the signal....and I'm pretty sure that 'should' work for 2 channels, but I don't think it would work properly for any more than that because if more than one, but less than all channels were being played, then the channels being played would have unequal amounts of time at the dac. I'm up for suggestions. Thanks again to Arriba for the envelope solution.
CON _clkmode = xtal1 + pll16x '5MHz * 16PLL = 80MHz system clock. _xinfreq = 5_000_000 '5MHz external crystal. VAR long cog 'cog id PUB start(freqPtr) : okay 'M is the tuning word stop okay := cog := cognew(@entry, freqPtr) 'Start DDS, uses a cog. PUB stop if cog 'If cog is running cogstop(cog~) 'Stop DDS, frees a cog DAT org 0 entry '256 sampled 8-bit sine table. Replace with any sampled waveform you like 'as long as the data table is of the same dimension and depth. Only longs are 'used to make the DDS loop as short as possible in terms of clock cycles. Sine long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8 long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5 long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7 long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3 long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83 long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51 long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27 long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00 long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08 long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23 long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C mov dira, PinMsk 'LSB to PA0, LSB to PA7 mov EnvPtr1, par add EnvPtr1, #24 'calc EnvAddr mov EnvPtr2, par add EnvPtr2, #28 'calc EnvAddr mov EnvPtr3, par add EnvPtr3, #32 'calc EnvAddr mov EnvPtr4, par add EnvPtr4, #36 'calc EnvAddr mov EnvPtr5, par add EnvPtr5, #40 'calc EnvAddr mov EnvPtr6, par add EnvPtr6, #44 'calc EnvAddr mov frq2,par add frq2,#4 mov frq3,par add frq3,#8 mov frq4,par add frq4,#12 mov frq5,par add frq5,#16 mov frq6,par add frq6,#20 :loop rdlong frq1,par wz 'Get Freq Control Value add Acc1, frq1 'DDS accumulate mov t1, Acc1 shr t1, #24 'use highest 8 bits as pointer movs :inline1, t1 ' into sine table nop :inline1 mov x, 0-0 'Get value from table (8 bit) rdlong y, EnvPtr1 'Get Env value (12 bit) call #mult 'sineVal * env shr y, #12 'divide by 4096 if_nz mov outa, y 'write to DAC at P0..P7 rdlong TW2,frq2 wz 'Get Freq Control Value add Acc2, TW2 'DDS accumulate mov t1, Acc2 shr t1, #24 'use highest 8 bits as pointer movs :inline2, t1 ' into sine table nop :inline2 mov x, 0-0 'Get value from table (8 bit) rdlong y, EnvPtr2 'Get Env value (12 bit) call #mult 'sineVal * env shr y, #12 'divide by 4096 if_nz mov outa, y 'write to DAC at P0..P7 {{ rdlong TW3,frq3 'Get Freq Control Value add Acc3, TW3 'DDS accumulate mov t1, Acc3 shr t1, #24 'use highest 8 bits as pointer movs :inline3, t1 ' into sine table nop :inline3 mov x, 0-0 'Get value from table (8 bit) rdlong y, EnvPtr3 'Get Env value (12 bit) call #mult 'sineVal * env shr y, #12 'divide by 4096 mov outa, y 'write to DAC at P0..P7 rdlong TW4, frq4 'Get Freq Control Value add Acc4, TW4 'DDS accumulate mov t1, Acc4 shr t1, #24 'use highest 8 bits as pointer movs :inline4, t1 ' into sine table nop :inline4 mov x, 0-0 'Get value from table (8 bit) rdlong y, EnvPtr4 'Get Env value (12 bit) call #mult 'sineVal * env shr y, #12 'divide by 4096 mov outa, y 'write to DAC at P0..P7 rdlong TW5, frq5 'Get Freq Control Value add Acc5, TW5 'DDS accumulate mov t1, Acc5 shr t1, #24 'use highest 8 bits as pointer movs :inline5, t1 ' into sine table nop :inline5 mov x, 0-0 'Get value from table (8 bit) rdlong y, EnvPtr5 'Get Env value (12 bit) call #mult 'sineVal * env shr y, #12 'divide by 4096 mov outa, y 'write to DAC at P0..P7 rdlong TW6,frq6 'Get Freq Control Value add Acc6, TW6 'DDS accumulate mov t1, Acc6 shr t1, #24 'use highest 8 bits as pointer movs :inline6, t1 ' into sine table nop :inline6 mov x, 0-0 'Get value from table (8 bit) rdlong y, EnvPtr6 'Get Env value (12 bit) call #mult 'sineVal * env shr y, #12 'divide by 4096 mov outa, y 'write to DAC at P0..P7 }} jmp #:loop 'Loop again '' Multiply x[15..0] by y[15..0] (y[31..16] must be 0) ' on exit, product in y[31..0] mult shl x,#16 'get multiplicand into x[31..16] mov t1,#16 'ready for 16 multiplier bits shr y,#1 wc 'get initial multiplier bit into c :loop if_c add y,x wc 'if c set, add multiplicand to product rcr y,#1 wc 'put next multiplier in c, shift prod. djnz t1,#:loop 'loop until done mult_ret ret 'return with product in y[31..0] PinMsk long $FF state long $100 frq1 long 0 'Tuning word var init Acc1 long 0 'Accumulator var init EnvPtr1 long 0 'Address of EnvValue (12bit) TW2 long 0 frq2 long 0 'Tuning word var init Acc2 long 0 'Accumulator var init EnvPtr2 long 0 'Address of EnvValue (12bit) frq3 long 0 'Tuning word var init Acc3 long 0 'Accumulator var init TW3 long 0 EnvPtr3 long 0 'Address of EnvValue (12bit) frq4 long 0 'Tuning word var init Acc4 long 0 'Accumulator var init TW4 long 0 EnvPtr4 long 0 'Address of EnvValue (12bit) frq5 long 0 'Tuning word var init Acc5 long 0 'Accumulator var init EnvPtr5 long 0 'Address of EnvValue (12bit) TW5 long 0 frq6 long 0 'Tuning word var init Acc6 long 0 'Accumulator var init EnvPtr6 long 0 'Address of EnvValue (12bit) TW6 long 0 x long 0 y long 0 t1 long 0And here is a sample picture of the output when 2 keys are pressed simultaneously:
Instead you have a write to outa for each scaled sine wave component.
The result is that the output is "chopping" from the value of one sine wave frequency to another, twice per loop, we can see this in your scope trace.
So perhaps instead of "mov outa, y" you should "add output, y" for each frequency component and then "mov outa, output" at the end of the loop.
Don't forget to zero output afterwards.
P.S. Don't forget to scale down the totalled output value as it may well have exceeded 8 bits. So for two frequencies shift right by one, for four frequencies shift right by two etc.
Oh, I was just noting that you introduced me to the idea of interleaving.....I just initially thought that's what you meant, I should have dug deaper on that...but that makes much more sense. Thanks, I will try that out asap.