TriOS/lib/adm-sid.spin

796 lines
44 KiB
Plaintext

{{
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ SIDcog - SID/MOS8580 emulator V0.80 (C) 2010-02 Johannes Ahlebrand │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ TERMS OF USE: Parallax Object Exchange License │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
│is furnished to do so, subject to the following conditions: │
│ │
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
│ │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}
{
11-07-2010-dr235 - änderung in setWaveform
}
CON
PAL = 985248.0
NTSC = 1022727.0
MAXF = 1031000.0
TRIANGLE = 16
SAW = 32
SQUARE = 64
NOISE = 128
C64_CLOCK_FREQ = PAL
' ___
CUTOFF_LIMIT = 1300' │
LP_MAX_CUTOFF = 10' │
BP_MAX_CUTOFF = 11' │ Don't alter these unless you know what you are doing. ;)
FILTER_OFFSET = 27' │
DECAY_DIVIDE_REF = $6C6C6C6C' │
NOISE_ADD = $979D21B5' │
NOISE_ROTATE = 28' ___│
PUB start(right,left)
arg1 := $18000000 | left
arg2 := $18000000 | right
r1 := ((1<<right) | (1<<left))&!1
sampleRate := clkfreq/trunc(C64_CLOCK_FREQ/32.0)
combTableAddr := @combinedWaveforms
cog := cognew(@SIDEMU, @ch1_frequencyLo) + 1
return @ch1_frequencyLo
PUB stop
if cog
cogstop(cog~ -1)
PUB setRegister(reg,val)
byte[@ch1_frequencyLo+(reg+(reg/7))] := val
PUB updateRegisters(source)
bytemove(@ch1_frequencyLo , source , 7)
bytemove(@ch1_frequencyLo+8 , source+7 , 7)
bytemove(@ch1_frequencyLo+16, source+14, 7)
bytemove(@ch1_frequencyLo+24, source+21, 4)
PUB getSample
return long[@SIDSample]
PUB setVolume(value)
byte[@Volume] := (byte[@Volume]&$F0) | (value&$0F)
PUB play(channel, freq, waveform, attack, decay, sustain, release) | offs
offs := channel<<3
word[@ch1_frequencyLo+offs] := freq
byte[@ch1_attackDecay+offs] := (decay&$F) | ((attack&$F)<<4)
byte[@ch1_sustainRelease+offs] := (release&$F) | ((sustain&$F)<<4)
byte[@ch1_controlRegister+offs] := (byte[@ch1_controlRegister+offs]&$0F) | waveform | 1
PUB noteOn(channel, freq) | offs
offs := channel<<3
byte[@ch1_controlRegister+offs] := (byte[@ch1_controlRegister+offs]&$FE) | 1
word[@ch1_frequencyLo+offs] := freq
PUB noteOff(channel)
byte[@ch1_controlRegister+(channel<<3)] &= $FE
PUB setFreq(channel,freq)
word[@ch1_frequencyLo+(channel<<3)] := freq
PUB setWaveform(channel,waveform) | offs
offs := channel<<3
byte[@ch1_controlRegister+offs] := (byte[@ch1_controlRegister+offs]&$0F) | waveform
' byte[@ch1_controlRegister+offs] := (byte[@ch1_controlRegister+offs]&$0F) | (1<<(4+(waveform&3)))
PUB setPWM(channel, value)
word[@ch1_pulseWidthLo+(channel<<3)] := value
PUB setADSR(channel, attack, decay, sustain, release ) | offs
offs := channel<<3
byte[@ch1_attackDecay+offs] := (decay&$F) | ((attack&$F)<<4)
byte[@ch1_sustainRelease+offs] := (release&$F) | ((sustain&$F)<<4)
PUB setResonance(val)
byte[@Filter3] := (byte[@Filter3]&$0F) | (val<<4)
PUB setCutoff(freq)
byte[@Filter1] := freq&$07
byte[@Filter2] := (freq&$07F8)>>3
PUB setFilterMask(ch1,ch2,ch3)
byte[@Filter3] := (byte[@Filter3]&$F0) | (ch1&1) | (ch2&2) | (ch3&4)
PUB setFilterType(lp,bp,hp)
byte[@volume] := (byte[@volume]&$0F) | (lp&16) | (bp&32) | (hp&64)
PUB enableRingmod(ch1,ch2,ch3)
byte[@ch1_controlRegister] := (byte[@ch1_controlRegister]&$FB) | (ch1&4)
byte[@ch2_controlRegister] := (byte[@ch2_controlRegister]&$FB) | (ch2&4)
byte[@ch3_controlRegister] := (byte[@ch3_controlRegister]&$FB) | (ch3&4)
PUB enableSynchronization(ch1,ch2,ch3)
byte[@ch1_controlRegister] := (byte[@ch1_controlRegister]&$FD) | (ch1&2)
byte[@ch2_controlRegister] := (byte[@ch2_controlRegister]&$FD) | (ch2&2)
byte[@ch3_controlRegister] := (byte[@ch3_controlRegister]&$FD) | (ch3&2)
dat org 0
'
' Assembly SID emulator
'
SIDEMU mov dira, r1
mov ctra, arg1
mov ctrb, arg2
mov waitCounter, cnt
add waitCounter, sampleRate
'-----------------------------------------------------------
mainLoop call #getRegisters
call #SID
call #filter
call #mixer
jmp #mainLoop
'-----------------------------------------------------------
'
' Read all SID-registers from hub memory and convert
' them to more convenient representations.
'
getRegisters mov tempValue, par ' Read in first long ( 16bit frequency / 16bit pulse-width )
rdlong frequency1, tempValue
mov pulseWidth1, frequency1
shl pulseWidth1, #4 ' Shift in "12 bit" pulse width value( make it 32 bits )
andn pulseWidth1, mask20bit
and frequency1, mask16bit ' Mask out 16 bit frequency value
shl frequency1, #13
'-----------------------------------------------------------
add tempValue, #4 ' Read in next long ( Control register / ADSR )
rdlong selectedWaveform1, tempValue
mov controlRegister1, selectedWaveform1
'-----------------------------------------------------------
mov arg1, selectedWaveform1 '|
shr arg1, #8 '|
call #getDecay '|
mov decay1, r1 '|
shr arg1, #4 '|
call #getAttack '|
mov attack1, r1 '| Convert 4bit ADSR "presets" to their corresponding
shr arg1, #4 '| 32bit values using attack/decay tables.
call #getDecay '|
mov release1, r1 '|
mov sustain1, selectedWaveform1 '|
shl sustain1, #8 '|
andn sustain1, mask28bit '|
'-----------------------------------------------------------
shr selectedWaveform1, #4 ' Mask out waveform selection
and selectedWaveform1, #15 wz, wc
'-----------------------------------------------------------
test controlRegister1, #1 wc
cmp envelopeState1, #2 wz
if_z_and_c mov envelopeState1, #0
if_nz_and_nc mov envelopeState1, #2
'───────────────────────────────────────────────────────────
' Channel 2
'───────────────────────────────────────────────────────────
add tempValue, #4 ' Read in first long ( 16bit frequency / 16bit pulse-width )
rdlong frequency2, tempValue
mov pulseWidth2, frequency2
shl pulseWidth2, #4 ' Shift in "12 bit" pulse width value( make it 32 bits )
andn pulseWidth2, mask20bit
and frequency2, mask16bit ' Mask out 16 bit frequency value
shl frequency2, #13
'-----------------------------------------------------------
add tempValue, #4 ' Read in next long ( Control register / ADSR )
rdlong selectedWaveform2, tempValue
mov controlRegister2, selectedWaveform2
'-----------------------------------------------------------
mov arg1, selectedWaveform2 '|
shr arg1, #8 '|
call #getDecay '|
mov decay2, r1 '|
shr arg1, #4 '|
call #getAttack '|
mov attack2, r1 '| Convert 4bit ADSR "presets" to their corresponding
shr arg1, #4 '| 32bit values using attack/decay tables.
call #getDecay '|
mov release2, r1 '|
mov sustain2, selectedWaveform2 '|
shl sustain2, #8 '|
andn sustain2, mask28bit '|
'-----------------------------------------------------------
shr selectedWaveform2, #4 ' Mask out waveform selection
and selectedWaveform2, #15 wz, wc
'-----------------------------------------------------------
test controlRegister2, #1 wc
cmp envelopeState2, #2 wz
if_z_and_c mov envelopeState2, #0
if_nz_and_nc mov envelopeState2, #2
'───────────────────────────────────────────────────────────
' Channel 3
'───────────────────────────────────────────────────────────
add tempValue, #4 ' Read in first long ( 16bit frequency / 16bit pulse-width )
rdlong frequency3, tempValue '
mov pulseWidth3, frequency3
shl pulseWidth3, #4 ' Shift in "12 bit" pulse width value( make it 32 bits )
andn pulseWidth3, mask20bit
and frequency3, mask16bit ' Mask out 16 bit frequency value
shl frequency3, #13
'-----------------------------------------------------------
add tempValue, #4 ' Read in next long ( Control register / ADSR )
rdlong selectedWaveform3, tempValue
mov controlRegister3, selectedWaveform3
'-----------------------------------------------------------
mov arg1, selectedWaveform3 '|
shr arg1, #8 '|
call #getDecay '|
mov decay3, r1 '|
shr arg1, #4 '|
call #getAttack '|
mov attack3, r1 '| Convert 4bit ADSR "presets" to their corresponding
shr arg1, #4 '| 32bit values using attack/decay tables.
call #getDecay '|
mov release3, r1 '|
mov sustain3, selectedWaveform3 '|
shl sustain3, #8 '|
andn sustain3, mask28bit '|
'-----------------------------------------------------------
shr selectedWaveform3, #4 ' Mask out waveform selection
and selectedWaveform3, #15 wz, wc
'-----------------------------------------------------------
test controlRegister3, #1 wc
cmp envelopeState3, #2 wz
if_z_and_c mov envelopeState3, #0
if_nz_and_nc mov envelopeState3, #2
'───────────────────────────────────────────────────────────
' Filter / Volume
'───────────────────────────────────────────────────────────
add tempValue, #4 '|
rdlong filterControl, tempValue '|
mov filterCutoff, filterControl '|
'-----------------------------------------------------------
shr filterControl, #16 '| Filter control
'-----------------------------------------------------------
shr filterCutoff, #5 '|
andn filterCutoff, #7 '|
mov tempValue, filterControl '|
and tempValue, #7 '| Filter cutoff frequency
or filterCutoff, tempValue '|
and filterCutoff, mask11bit '|
add filterCutoff, filterOffset '|
'-----------------------------------------------------------
mov filterMode_Volume, filterControl '| Main volume and filter mode
shr filterMode_Volume, #8 '|
'-----------------------------------------------------------
mov filterResonance,filterControl '|
and filterResonance,#$F0 '|
shr filterResonance,#5 '|
add filterResonance,#resTable '| Filter Resonance level (Ariba)
movs rdRes,filterResonance '|
nop '|
rdRes mov filterResonance,0-0 '|
getRegisters_ret ret
'
' Calculate sid samples channel 1-3 and store in out1-out3
'
'───────────────────────────────────────────────────────────
' Increment phase accumulator 1-3 and handle syncing
'───────────────────────────────────────────────────────────
SID add phaseAccumulator1, frequency1 wc ' Add frequency value to phase accumulator 1
if_nc andn controlRegister2, #2
test controlRegister2, #10 wz ' Sync oscilator 2 to oscilator 1 if sync = on
if_nz mov phaseAccumulator2, #0 ' Or reset counter 2 when bit 4 of control register is 1
'-----------------------------------------------------------
add phaseAccumulator2, frequency2 wc
if_nc andn controlRegister3, #2
test controlRegister3, #10 wz ' Sync oscilator 3 to oscilator 2 if sync = on
if_nz mov phaseAccumulator3, #0 ' Or reset oscilator 3 when bit 4 of control register is 1
'-----------------------------------------------------------
add phaseAccumulator3, frequency3 wc
if_nc andn controlRegister1, #2
test controlRegister1, #10 wz ' Sync oscilator 1 to oscilator 3 if sync = on
if_nz mov phaseAccumulator1, #0 ' Or reset oscilator 1 when bit 4 of control register is 1
'───────────────────────────────────────────────────────────
' Waveform shaping channel 1 -> arg1
'───────────────────────────────────────────────────────────
Triangle1 tjz selectedWaveform1, #Triangle2
cmp selectedWaveform1, #1 wz
if_nz jmp #Saw1
abs arg1, phaseAccumulator1
max arg1, mask31bit
test controlRegister1, #4 wc '|
if_c test phaseAccumulator3, val31bit wc '| These 3 lines handles ring modulation
if_c xor arg1, mask32bit '|
shl arg1, #1
jmp #Env_Attack1
'-----------------------------------------------------------
Saw1 cmp selectedWaveform1, #2 wz
if_z mov arg1, phaseAccumulator1
if_z jmp #Env_Attack1
'-----------------------------------------------------------
Square1 cmp selectedWaveform1, #4 wz
if_z sub pulseWidth1, phaseAccumulator1 wc ' C holds the pulse width modulated square wave
if_z muxc arg1, mask32bit
if_z jmp #Env_Attack1
'-----------------------------------------------------------
Noise1 cmp selectedWaveform1, #8 wz
if_nz jmp #Combined1
mov tempValue, phaseAccumulator1
and tempValue, mask28bit
sub tempValue, frequency1 wc
if_z_and_c ror noiseValue1, NOISE_ROTATE
if_z_and_c add noiseValue1, noiseAdd
if_z mov arg1, noiseValue1
jmp #Env_Attack1
'-----------------------------------------------------------
Combined1 sub selectedWaveform1, #4
mins selectedWaveform1, #0
shl selectedWaveform1, #8
mov tempValue, phaseAccumulator1
shr tempValue, #24
add selectedWaveform1, tempValue
add selectedWaveform1, combTableAddr
rdbyte arg1, selectedWaveform1
shl arg1, #24
'───────────────────────────────────────────────────────────
' Envelope shaping channel 1 -> arg2
'───────────────────────────────────────────────────────────
Env_Attack1 cmp envelopeState1, #0 wz
if_nz jmp #InitDecay1
add envelopeLevel1, attack1 wc
if_c mov envelopeLevel1, mask32bit
if_c mov envelopeState1, #1
mov decayDivide1, #0
jmp #Amplitude1
'-----------------------------------------------------------
InitDecay1 mov tempValue, decayDivideRef
shr tempValue, decayDivide1
cmp envelopeLevel1, tempValue wc
if_c add decayDivide1, #1
'-----------------------------------------------------------
Env_Decay1 cmp envelopeState1, #1 wz
if_nz jmp #Env_Release1
shr decay1, decayDivide1
sub envelopeLevel1, decay1
min envelopeLevel1, sustain1 wc
jmp #Amplitude1
'-----------------------------------------------------------
Env_Release1 cmp envelopeState1, #2 wz
if_nz jmp #Amplitude1
shr release1, decayDivide1
cmpsub envelopeLevel1, release1
'───────────────────────────────────────────────────────────
'Calculate sample out1 = arg1 * arg2 (waveform * amplitude)
'───────────────────────────────────────────────────────────
Amplitude1 shr arg1, #16
sub arg1, val15bit
mov arg2, envelopeLevel1
shr arg2, #22
call #multiply
mov out1, r1
'───────────────────────────────────────────────────────────
' Waveform shaping channel 2 -> arg1
'───────────────────────────────────────────────────────────
Triangle2 tjz selectedWaveform2, #Triangle3
cmp selectedWaveform2, #1 wz
if_nz jmp #Saw2
abs arg1, phaseAccumulator2
max arg1, mask31bit
test controlRegister2, #4 wc '|
if_c test phaseAccumulator1, val31bit wc '| These 3 lines handles ring modulation
if_c xor arg1, mask32bit '|
shl arg1, #1
jmp #Env_Attack2
'-----------------------------------------------------------
Saw2 cmp selectedWaveform2, #2 wz
if_z mov arg1, phaseAccumulator2
if_z jmp #Env_Attack2
'-----------------------------------------------------------
Square2 cmp selectedWaveform2, #4 wz
if_z sub pulseWidth2, phaseAccumulator2 wc ' C holds the pulse width modulated square wave
if_z muxc arg1, mask32bit
if_z jmp #Env_Attack2
'-----------------------------------------------------------
Noise2 cmp selectedWaveform2, #8 wz
if_nz jmp #Combined2
mov tempValue, phaseAccumulator2
and tempValue, mask28bit
sub tempValue, frequency2 wc
if_z_and_c ror noiseValue2, NOISE_ROTATE
if_z_and_c add noiseValue2, noiseAdd
if_z mov arg1, noiseValue2
jmp #Env_Attack2
'-----------------------------------------------------------
Combined2 sub selectedWaveform2, #4
mins selectedWaveform2, #0
shl selectedWaveform2, #8
mov tempValue, phaseAccumulator2
shr tempValue, #24
add selectedWaveform2, tempValue
add selectedWaveform2, combTableAddr
rdbyte arg1, selectedWaveform2
shl arg1, #24
'───────────────────────────────────────────────────────────
' Envelope shaping channel 2 -> arg2
'───────────────────────────────────────────────────────────
Env_Attack2 cmp envelopeState2, #0 wz
if_nz jmp #InitDecay2
add envelopeLevel2, attack2 wc
if_c mov envelopeLevel2, mask32bit
if_c mov envelopeState2, #1
mov decayDivide2, #0
jmp #Amplitude2
'-----------------------------------------------------------
InitDecay2 mov tempValue, decayDivideRef
shr tempValue, decayDivide2
cmp envelopeLevel2, tempValue wc
if_c add decayDivide2, #1
'-----------------------------------------------------------
Env_Decay2 cmp envelopeState2, #1 wz
if_nz jmp #Env_Release2
shr decay2, decayDivide2
sub envelopeLevel2, decay2
min envelopeLevel2, sustain2 wc
jmp #Amplitude2
'-----------------------------------------------------------
Env_Release2 cmp envelopeState2, #2 wz
if_nz jmp #Amplitude2
shr release2, decayDivide2
cmpsub envelopeLevel2, release2
'───────────────────────────────────────────────────────────
'Calculate sample out2 = arg1 * arg2 (waveform * amplitude)
'───────────────────────────────────────────────────────────
Amplitude2 shr arg1, #16
sub arg1, val15bit
mov arg2, envelopeLevel2
shr arg2, #22
call #multiply
mov out2, r1
'───────────────────────────────────────────────────────────
' Waveform shaping channel 3 -> arg1
'───────────────────────────────────────────────────────────
Triangle3 tjz selectedWaveform3, #SID_ret
cmp selectedWaveform3, #1 wz
if_nz jmp #Saw3
abs arg1, phaseAccumulator3
max arg1, mask31bit
test controlRegister3, #4 wc '|
if_c test phaseAccumulator2, val31bit wc '| These 3 lines handles ring modulation
if_c xor arg1, mask32bit '|
shl arg1, #1
jmp #Env_Attack3
'-----------------------------------------------------------
Saw3 cmp selectedWaveform3, #2 wz
if_z mov arg1, phaseAccumulator3
if_z jmp #Env_Attack3
'-----------------------------------------------------------
Square3 cmp selectedWaveform3, #4 wz
if_z sub pulseWidth3, phaseAccumulator3 wc ' C holds the pulse width modulated square wave
if_z muxc arg1, mask32bit
if_z jmp #Env_Attack3
'-----------------------------------------------------------
Noise3 cmp selectedWaveform3, #8 wz
if_nz jmp #Combined3
mov tempValue, phaseAccumulator3
and tempValue, mask28bit
sub tempValue, frequency3 wc
if_z_and_c ror noiseValue3, NOISE_ROTATE
if_z_and_c add noiseValue3, noiseAdd
if_z mov arg1, noiseValue3
jmp #Env_Attack3
'-----------------------------------------------------------
Combined3 sub selectedWaveform3, #4
mins selectedWaveform3, #0
shl selectedWaveform3, #8
mov tempValue, phaseAccumulator3
shr tempValue, #24
add selectedWaveform3, tempValue
add selectedWaveform3, combTableAddr
rdbyte arg1, selectedWaveform3
shl arg1, #24
'───────────────────────────────────────────────────────────
' Envelope shaping channel 3 -> arg2
'───────────────────────────────────────────────────────────
Env_Attack3 cmp envelopeState3, #0 wz
if_nz jmp #InitDecay3
add envelopeLevel3, attack3 wc
if_c mov envelopeLevel3, mask32bit
if_c mov envelopeState3, #1
mov decayDivide3, #0
jmp #Amplitude3
'-----------------------------------------------------------
InitDecay3 mov tempValue, decayDivideRef
shr tempValue, decayDivide3
cmp envelopeLevel3, tempValue wc
if_c add decayDivide3, #1
'-----------------------------------------------------------
Env_Decay3 cmp envelopeState3, #1 wz
if_nz jmp #Env_Release3
shr decay3, decayDivide3
sub envelopeLevel3, decay3
min envelopeLevel3, sustain3 wc
jmp #Amplitude3
'-----------------------------------------------------------
Env_Release3 cmp envelopeState3, #2 wz
if_nz jmp #Amplitude3
shr release3, decayDivide3
cmpsub envelopeLevel3, release3
'───────────────────────────────────────────────────────────
'Calculate sample out3 = arg1 * arg2 (waveform * amplitude)
'───────────────────────────────────────────────────────────
Amplitude3 shr arg1, #16
sub arg1, val15bit
mov arg2, envelopeLevel3
shr arg2, #22
call #multiply
mov out3, r1
SID_ret ret
'
' Handle multi-mode filtering
'
filter mov ordinaryOutput, #0 '|
mov highPassFilter, #0 '|
test filterControl, #1 wc '|
if_c add highPassFilter, out1 '|
if_nc add ordinaryOutput, out1 '|
test filterControl, #2 wc '| Route channels trough the filter
if_c add highPassFilter, out2 '| or bypass them
if_nc add ordinaryOutput, out2 '|
test filterControl, #4 wc '|
if_c add highPassFilter, out3 '|
if_nc add ordinaryOutput, out3 '|
'-----------------------------------------------------------
mov tempValue, bandPassFilter '|
sar tempValue, filterResonance '| High pass filter
sub highPassFilter, tempValue '|
sub highPassFilter, lowPassFilter '|
'-----------------------------------------------------------
mov arg1, highPassFilter '|
sar arg1, #BP_MAX_CUTOFF '|
mov arg2, filterCutoff '| Band pass filter
max arg2, maxCutoff
call #multiplyNS '|
add bandPassFilter, r1 '|
'-----------------------------------------------------------
mov arg1, bandPassFilter '|
sar arg1, #LP_MAX_CUTOFF '|
mov arg2, filterCutoff '| Low pass filter
call #multiplyNS '|
add lowPassFilter, r1 '|
'-----------------------------------------------------------
mov filterOutput, #0 '|
test filterMode_Volume, #16 wc '|
if_c add filterOutput, lowPassFilter '|
test filterMode_Volume, #32 wc '| Enable/Disable
if_c add filterOutput, bandPassFilter '| Low/Band/High pass filtering
test filterMode_Volume, #64 wc '|
if_c add filterOutput, highPassFilter '|
filter_ret ret
'
' Mix channels and update FRQA/FRQB PWM-values
'
mixer mov arg1, filterOutput
add arg1, ordinaryOutput
'-----------------------------------------------------------
maxs arg1, clipLevelHigh '|
mins arg1, clipLevelLow '|
mov arg2, filterMode_Volume '| Main volume adjustment
and arg2, #15 '|
call #multiplyNS '|
'-----------------------------------------------------------
add r1, val31bit ' DC offset
waitcnt waitCounter, sampleRate ' Wait until the right time to update
mov FRQA, r1 '| Update PWM values in FRQA/FRQB
mov FRQB, r1 '|
mov tempValue, par
add tempValue, #28
wrlong r1, tempValue '| Write the sample to hub ram
mixer_ret ret
'
' Get attack value r1 = attackTable[arg1]
'
getAttack mov arg2, arg1
and arg1, #15
add arg1, #ADSRTable
movs :indexed1, arg1
mov arg1, arg2
:indexed1 mov r1, 0
getAttack_ret ret
'
' Get decay value r1 = decayTable[arg1]
'
getDecay mov arg2, arg1
and arg1, #15
add arg1, #ADSRTable
movs :indexed2, arg1
mov arg1, arg2
:indexed2 mov r1, 0
getDecay_ret ret
'
' Multiplication r1(I32) = arg1(I32) * arg2(I32)
'
multiply cmp arg1, arg2 wc 'If arg1 is less than arg2 C is set
if_c xor arg1, arg2 'Swap arguments
if_c xor arg2, arg1
if_c xor arg1, arg2
multiplyNS mov r1, #0 'Clear 32-bit product
:multiLoop shr arg2, #1 wc, wz 'Half multiplyer and get LSB of it
if_c add r1, arg1 'Add multiplicand to product on C
shl arg1, #1 'Double multiplicand
if_nz jmp #:multiLoop 'Check nonzero multiplier to continue multiplication
multiplyNS_ret
multiply_ret ret
'
' Variables, tables, masks and reference values
'
ADSRTable long 68719476 '2 ms
long 17179870 '8 ms
long 8589934 '16 ms
long 5726622 '24 ms
long 3616814 '38 ms
long 2454268 '56 ms
long 2021160 '68 ms
long 1717986 '80 ms
long 1374390 '100 ms
long 549754 '250 ms
long 274876 '500 ms
long 171798 '800 ms
long 137438 '1 s
long 45812 '3 s
long 27486 '5 s
long 17180 '8 s
resTable long 0,1,1,1,1,1,2,2 '(Ariba)
'Masks and reference values
mask32bit long $ffffffff
mask31bit long $7fffffff
mask28bit long $fffffff
mask24bit long $ffffff
mask20bit long $fffff
mask16bit long $ffff
mask11bit long $7ff
val31bit long $80000000
val28bit long $10000000
val27bit long $8000000
val16bit long $10000
val15bit long $8000
clipLevelHigh long $8000000
clipLevelLow long-$8000000
noiseAdd long NOISE_ADD 'Value to add to the noise generator every noise update
filterOffset long FILTER_OFFSET
decayDivideRef long DECAY_DIVIDE_REF
maxCutoff long CUTOFF_LIMIT
sampleRate long 0 'clocks between samples ( ~31.250 khz )
combTableAddr long 0
'Setup and subroutine parameters
arg1 long 1
arg2 long 1
r1 long 1
'Sid variables
envelopeLevel1 long 1
envelopeLevel2 long 1
envelopeLevel3 long 1
controlRegister1 res 1
controlRegister2 res 1
controlRegister3 res 1
frequency1 res 1
frequency2 res 1
frequency3 res 1
phaseAccumulator1 res 1
phaseAccumulator2 res 1
phaseAccumulator3 res 1
pulseWidth1 res 1
pulseWidth2 res 1
pulseWidth3 res 1
selectedWaveform1 res 1
selectedWaveform2 res 1
selectedWaveform3 res 1
envelopeState1 res 1
envelopeState2 res 1
envelopeState3 res 1
noiseValue1 res 1
noiseValue2 res 1
noiseValue3 res 1
attack1 res 1
attack2 res 1
attack3 res 1
decay1 res 1
decay2 res 1
decay3 res 1
sustain1 res 1
sustain2 res 1
sustain3 res 1
release1 res 1
release2 res 1
release3 res 1
decayDivide1 res 1
decayDivide2 res 1
decayDivide3 res 1
out1 res 1
out2 res 1
out3 res 1
filterResonance res 1
filterCutoff res 1
highPassFilter res 1
bandPassFilter res 1
lowPassFilter res 1
filterMode_Volume res 1
filterControl res 1
filterOutput res 1
ordinaryOutput res 1
'Working variables
waitCounter res 1
tempValue res 1
fit
dat
combinedWaveforms file "adm-sid-combined-waveforms.bin"
VAR
byte ch1_frequencyLo
byte ch1_frequencyHi
byte ch1_pulseWidthLo
byte ch1_pulseWidthHi
byte ch1_controlRegister
byte ch1_attackDecay
byte ch1_sustainRelease
byte ch1_dummy
byte ch2_frequencyLo
byte ch2_frequencyHi
byte ch2_pulseWidthLo
byte ch2_pulseWidthHi
byte ch2_controlRegister
byte ch2_attackDecay
byte ch2_sustainRelease
byte ch2_dummy
byte ch3_frequencyLo
byte ch3_frequencyHi
byte ch3_pulseWidthLo
byte ch3_pulseWidthHi
byte ch3_controlRegister
byte ch3_attackDecay
byte ch3_sustainRelease
byte ch3_dummy
byte Filter1
byte Filter2
byte Filter3
byte Volume
long SIDSample
byte cog