TACHYON [~
DECIMAL
IFNDEF SIDEMU
." SIDEMU and CombinedWaveForms binary must be loaded first !!! "
}
FORGET SIDcog.fth
pub SIDcog.fth ." 140107-2230 Tachyon Forth adaption by PBJ of SID/MOS8580 emulator v1.3 (C) 2012 Johannes Ahlebrand" ;
{
The SIDcog interface has been restructured so that it is easier to interact with the SID.
Sounds can be produced and changed just from the serial console.
i.e.
0 CHAN 8 3 4 2 ADSR ON HP ON BP OFF LP ON SAWTOOTH #1000 NOTE
or
: DEMO 0 CHAN 8 3 4 2 ADSR ON HP ON BP OFF LP ON SAWTOOTH #1000 NOTE ;
then type DEMO to play that effect
NOTE: this code is incomplete and quite possible ridden with typos etc - will test this out soon
TO DO: Generate table for PASM section (WIP - extracting code bytes from BST listing)
}
( The original 6581 SID chip block diagram )
( SID CHANNEL REGISTERS )
\ Create a long aligned byte array table for all the SID variables which are read by the SID cog
32 BYTES sidtbl
0 ORG \ compile as channel offsets (computed as offsets in sidtbl)
2 DS freq
2 DS pulseWidth
1 DS control
1 DS ad
1 DS sr
\ next 2 channels
7 DS+ \ channel 2
7 DS+ \ channel 3
( SID FILTERS )
sidtbl 21 + ORG \ compile these as absolute addresses
1 DS filterLo \ lowpass cutoff
1 DS filterHi \ highpass cutoff
1 DS filterRes \ filter enables + resonance
1 DS volume \ filter selects + volume
1 DS oldVolume
LONG SIDSample,cog
\ function to convert 2 nibbles into a byte
pri NIB>BYTE ( highnib lownib -- byte )
$0F AND SWAP $0F AND 4 SHL OR \ (release&$F) | ((sustain&$F)<<4)
;
\ Set or reset the mask at addr depending upon state (true=set)
pri BIT ( state mask addr -- )
ROT BIT!
;
WORD sidadr \ holds the base address of the current channel
pub CHAN ( channel -- )
7 * sidtbl + sidadr W! \ Set base address
;
pub @SID ( offset -- addr ) sidadr W@ + ;
( setADSR )
pub ADSR ( attack, decay, sustain, release -- )
{ Sets the envelope values of a SID channel
attack - The attack value. (0 - 15)
decay - The decay value. (0 - 15)
sustain - The sustain value. (0 - 15)
release - The release value. (0 - 15)
}
NIB>BYTE sr @SID C!
NIB>BYTE ad @SID C!
;
( setPulseWidth )
pub PULSE ( pulseWidth -- )
{ Sets the pulse width of the preselected SID channel
pulseWidth - The 12 bit pulse width value to use. (0 - 4095)
- The pulse width value affects square waves ONLY.
PWout = (pulseWidth/40.95)%
}
pulseWidth @SID W!
;
( setFreq )
pub FREQ ( freq -- )
{ Sets the frequency of the preselected SID channel
freq - The 16 bit frequency value. (0 - 65535)
(The SID can output tone frequencies from 0 - 3.9 kHz)
}
freq @SID W!
;
( updateRegisters )
pub UPDATE ( source -- )
{ Update all 25 SID registers
source - A pointer to an array containing 25 bytes to update
the 25 SID registers with.
}
sidtbl 25 CMOVE
;
( resetRegisters )
pub !SID
{ Reset all 25 SID registers }
sidtbl 25 ERASE
;
pub VOLUME ( volume -- )
{ Sets the main volume
value - A value betwen 0 and 15.
}
$0F AND volume C@ $0F ANDN OR volume C!
;
( FILTER REGISTERS )
( setResonance )
pub RESONANCE ( resonanceValue -- )
{ Sets the resonance value of the filter
resonanceValue - The resonance value to use. (0 - 15)
}
4 SHL filterRes C@ $0F AND OR filterRes C!
;
pub CUTLOW ( lowcutoff -- )
7 AND filterLo C!
;
( setCutoff )
pub CUTOFF ( cutoffValue -- )
{ Sets the cutoff frequency of the 12-bit filter }
DUP CUTLOW 3 SHR
pub CUTHIGH ( hicutoff -- )
filterHi C!
;
( setFilterMask )
pub FILTERMASK ( mask --- )
{ Enable/Disable filtering on channels }
filterRes C@ $0F ANDN OR filterRes C!
;
( setFilterType )
pub FILTERS ( mask -- ) ( b0=lp b1 =bp b2=hp b3=3off )
{ Enable/Disable filter types using additive bit-field masks }
4 SHL volume C@ $0F AND OR volume C!
;
pub HP ( on/off -- ) \ Turn high-pass filter on or off
$40 volume BIT
;
pub BP ( on/off -- )
$20 volume BIT
;
pub LP ( on/off -- )
$10 volume BIT
;
( CONTROL REGISTERS - each channel )
\ control register bit field masks - Multiple waveforms are ANDed, not added
$80 == &NOISE
$40 == &SQUARE \ Select SQUARE wave
$20 == &SAW
$10 == &TRIANGLE
4 == &RING
2 == &SYNCH \ Sync with OSC3
1 == &GATE \ ADSR is triggered, or release
pub NOISE ( on/off -- )
&NOISE
pri CTL! ( on/off mask -- ) \ set or reset control bit
control @SID BIT
;
pub SQUARE ( on/off -- )
&SQUARE CTL!
;
pub SAWTOOTH ( on/off -- ) \ Turn on or off SAWTOOTH for the preselected channel
&SAW CTL!
;
pub TRIANGLE ( on/off -- )
&TRIANGLE CTL!
;
pub RINGMOD ( on/off -- )
&RING CTL!
;
pub SYNCH ( on/off -- )
&SYNC CTL!
;
pub GATE ( on/off -- )
&GATE CTL!
;
( setWaveform )
pub WAVE ( waveform -- )
{ Sets the waveform of the preselected SID channel
waveform - The waveform combination to use.
e.g. sid.setWaveform(x, sid#SQUARE | sid#SAW)
}
control @SID C@ $0F AND OR control @SID C!
;
( enableRingmod )
pub RINGMODS ( chmask -- )
{ Enable/Disable ring modulation on channels
- Channel 3 modulates channel 1
- Channel 1 modulates channel 2
- Channel 2 modulates channel 3
}
&RING
pri CONTROLS ( chmask regmask -- )
3 0 DO OVER I MASK AND OVER control sidtbl + I 7 * + BIT LOOP
2DROP
;
( enableSynchronization )
pub SYNCHS ( chmask -- )
{ Enable/Disable oscillator synchronization on channels
- Channel 3 synchronizes channel 1
- Channel 1 synchronizes channel 2
- Channel 2 synchronizes channel 3
}
&SYNCH CONTROLS
;
pub PLAY ( waveform, freq, attack, decay, sustain, release -- )
{ Plays a tone in a SID channel.
( The channel is selected beforehand using CHAN )
freq - The 16 bit frequency value use. (0 - 65535)
(The SID can output tone frequencies from 0 - 3.9 kHz)
waveform - The waveform combination to use.
e.g. sid.play(x, x, sid#SQUARE | sid#SAW, x, x, x, x)
attack - The attack value. (0 - 15)
decay - The decay value. (0 - 15)
sustain - The sustain value. (0 - 15)
release - The release value. (0 - 15)
When calling this method, the envelope generator enters the
"attack - decay - sustain" phase. Don't forget to call
"noteOff" before using it so the envelope is in release phase
}
ADSR FREQ
WAVE \ activate last to play sound
;
pub NOTE ( freq -- )
{ Plays a tone in a SID channel
freq - The 16 bit frequency value use. (0 - 65535)
*NB - a freq of 0 will mute the channel (OFF NOTE)
(The SID can output tone frequencies from 0 - 3.9 kHz)
- Don't forget to set the envelope values for the channel
before using this method.
- Make sure you have set the waveform for the channel before
using this method.
- When calling this method, the envelope generator enters the
"attack - decay - sustain" phase. Don't forget to call
"noteOff" before calling this method to set the envelope to
release phase.
}
DUP FREQ
&GATE CTL!
;
\ startSID
pub RUNSID ( right, left -- )
{ Starts SIDcog in a single cog
Returns a pointer to the first SID register in hub memory
on success; otherwise returns 0.
right - The pin to output the right channel to. 0 = Not used
left - The pin to output the left channel to. 0 = Not used
}
DUP $18000000 OR arg1 !
OVER $18000000 OR arg2 !
MASK SWAP MASK OR r1 !
CLKFREQ C64_CLOCK_FREQ 5 SHL / sampleRate !
combinedWaveFortms combTableAddr !
SIDEMU sidtbl COGNEW ( code pars -- ret )
1+ ?DUP IF freq W@ ELSE 0 THEN
;
{ PASM object loader - reads code bytes from BST listing
\ redefine | temporarily so that it compiles 4 bytes into the table
pri |
DEPTH 4 =>
IF
>L ROT | SWAP | | L> | \ compile 4 bytes into current location (table)
THEN
[COMPILE] \ \ ignore rest of line - we just want the 4 bytes
;
IMMEDIATE
' NOOP unum W! \ nullify number exception processing - TO DO: needs to be able to recognize numbers that begin with A-F
!SP \ clean up stack so we can detect depth
TABLE SIDEMU \ create a table to hold the assembled PASM image
\ -------> CODE BYTES <------ this is all we are interested in extracting
0018(0000) 93 ED BF A0 | SIDEMU mov dira, r1
001C(0001) 91 F1 BF A0 | mov ctra, arg1
0020(0002) 92 F3 BF A0 | mov ctrb, arg2
0024(0003) F1 93 BF A0 | mov waitCounter, cnt
0028(0004) 8F 93 BF 80 | add waitCounter, sampleRate
002C(0005) F0 95 BF A0 | getRegisters mov tempValue, par ' Read in first long ( 16bit frequency / 16bit pulse-width )
0030(0006) CA 45 BF 08 | rdlong frequency1, tempValue
0034(0007) A2 51 BF A0 | mov pulseWidth1, frequency1
0038(0008) 04 50 FF 2C | shl pulseWidth1, #4 ' Shift in "12 bit" pulse width value( make it 32 bits )
003C(0009) 82 51 BF 64 | andn pulseWidth1, mask20bit
0040(000A) 83 45 BF 60 | and frequency1, mask16bit ' Mask out 16 bit frequency value
0044(000B) 0D 44 FF 2C | shl frequency1, #13
0048(000C) 04 94 FF 80 | add tempValue, #4 ' Read in next long ( Control register / ADSR )
004C(000D) CA 57 BF 08 | rdlong selectedWaveform1, tempValue
0050(000E) AB 3F BF A0 | mov controlRegister1, selectedWaveform1
0054(000F) AB 23 BF A0 | mov arg1, selectedWaveform1 '|
0058(0010) 08 22 FF 28 | shr arg1, #8 '|
005C(0011) 60 CB FE 5C | call #getADSR '|
0060(0012) 93 69 BF A0 | mov decay1, r1 '|
0064(0013) 60 CB FE 5C | call #getADSR '|
0068(0014) 93 63 BF A0 | mov attack1, r1 '| Convert 4bit ADSR "presets" to their corresponding
006C(0015) 60 CB FE 5C | call #getADSR '| 32bit values using attack/decay tables.
0070(0016) 93 75 BF A0 | mov release1, r1 '|
0074(0017) 91 6F BF A0 | mov sustain1, arg1 '|
0078(0018) 04 6E FF 20 | ror sustain1, #4 '|
007C(0019) 91 6F BF 68 | or sustain1, arg1 '|
0080(001A) 04 6E FF 20 | ror sustain1, #4 '|
0084(001B) 04 56 FF 28 | shr selectedWaveform1, #4 ' Mask out waveform selection
0088(001C) 0F 56 FF 60 | and selectedWaveform1, #15
008C(001D) 01 3E 7F 61 | test controlRegister1, #1 wc
0090(001E) 02 5C 7F 86 | cmp envelopeState1, #2 wz
0094(001F) 00 5C E3 A0 | if_z_and_c mov envelopeState1, #0
0098(0020) 02 5C C7 A0 | if_nz_and_nc mov envelopeState1, #2
009C(0021) 04 94 FF 80 | add tempValue, #4 ' Read in first long ( 16bit frequency / 16bit pulse-width )
00A0(0022) CA 47 BF 08 | rdlong frequency2, tempValue
00A4(0023) A3 53 BF A0 | mov pulseWidth2, frequency2
00A8(0024) 04 52 FF 2C | shl pulseWidth2, #4 ' Shift in "12 bit" pulse width value( make it 32 bits )
00AC(0025) 82 53 BF 64 | andn pulseWidth2, mask20bit
00B0(0026) 83 47 BF 60 | and frequency2, mask16bit ' Mask out 16 bit frequency value
00B4(0027) 0D 46 FF 2C | shl frequency2, #13
00B8(0028) 04 94 FF 80 | add tempValue, #4 ' Read in next long ( Control register / ADSR )
00BC(0029) CA 59 BF 08 | rdlong selectedWaveform2, tempValue
00C0(002A) AC 41 BF A0 | mov controlRegister2, selectedWaveform2
00C4(002B) AC 23 BF A0 | mov arg1, selectedWaveform2 '|
00C8(002C) 08 22 FF 28 | shr arg1, #8 '|
00CC(002D) 60 CB FE 5C | call #getADSR '|
00D0(002E) 93 6B BF A0 | mov decay2, r1 '|
00D4(002F) 60 CB FE 5C | call #getADSR '|
00D8(0030) 93 65 BF A0 | mov attack2, r1 '| Convert 4bit ADSR "presets" to their corresponding
00DC(0031) 60 CB FE 5C | call #getADSR '| 32bit values using attack/decay tables.
00E0(0032) 93 77 BF A0 | mov release2, r1 '|
00E4(0033) 91 71 BF A0 | mov sustain2, arg1 '|
00E8(0034) 04 70 FF 20 | ror sustain2, #4 '|
00EC(0035) 91 71 BF 68 | or sustain2, arg1 '|
00F0(0036) 04 70 FF 20 | ror sustain2, #4 '|
00F4(0037) 04 58 FF 28 | shr selectedWaveform2, #4 ' Mask out waveform selection
00F8(0038) 0F 58 FF 60 | and selectedWaveform2, #15
00FC(0039) 01 40 7F 61 | test controlRegister2, #1 wc
0100(003A) 02 5E 7F 86 | cmp envelopeState2, #2 wz
0104(003B) 00 5E E3 A0 | if_z_and_c mov envelopeState2, #0
0108(003C) 02 5E C7 A0 | if_nz_and_nc mov envelopeState2, #2
010C(003D) 04 94 FF 80 | add tempValue, #4 ' Read in first long ( 16bit frequency / 16bit pulse-width )
0110(003E) CA 49 BF 08 | rdlong frequency3, tempValue '
0114(003F) A4 55 BF A0 | mov pulseWidth3, frequency3
0118(0040) 04 54 FF 2C | shl pulseWidth3, #4 ' Shift in "12 bit" pulse width value( make it 32 bits )
011C(0041) 82 55 BF 64 | andn pulseWidth3, mask20bit
0120(0042) 83 49 BF 60 | and frequency3, mask16bit ' Mask out 16 bit frequency value
0124(0043) 0D 48 FF 2C | shl frequency3, #13
0128(0044) 04 94 FF 80 | add tempValue, #4 ' Read in next long ( Control register / ADSR )
012C(0045) CA 5B BF 08 | rdlong selectedWaveform3, tempValue
0130(0046) AD 43 BF A0 | mov controlRegister3, selectedWaveform3
0134(0047) AD 23 BF A0 | mov arg1, selectedWaveform3 '|
0138(0048) 08 22 FF 28 | shr arg1, #8 '|
013C(0049) 60 CB FE 5C | call #getADSR '|
0140(004A) 93 6D BF A0 | mov decay3, r1 '|
0144(004B) 60 CB FE 5C | call #getADSR '|
0148(004C) 93 67 BF A0 | mov attack3, r1 '| Convert 4bit ADSR "presets" to their corresponding
014C(004D) 60 CB FE 5C | call #getADSR '| 32bit values using attack/decay tables.
0150(004E) 93 79 BF A0 | mov release3, r1 '|
0154(004F) 91 73 BF A0 | mov sustain3, arg1 '|
0158(0050) 04 72 FF 20 | ror sustain3, #4 '|
015C(0051) 91 73 BF 68 | or sustain3, arg1 '|
0160(0052) 04 72 FF 20 | ror sustain3, #4 '|
0164(0053) 04 5A FF 28 | shr selectedWaveform3, #4 ' Mask out waveform selection
0168(0054) 0F 5A FF 60 | and selectedWaveform3, #15
016C(0055) 01 42 7F 61 | test controlRegister3, #1 wc
0170(0056) 02 60 7F 86 | cmp envelopeState3, #2 wz
0174(0057) 00 60 E3 A0 | if_z_and_c mov envelopeState3, #0
0178(0058) 02 60 C7 A0 | if_nz_and_nc mov envelopeState3, #2
017C(0059) 04 94 FF 80 | add tempValue, #4 '|
0180(005A) CA 8D BF 08 | rdlong filterControl, tempValue '|
0184(005B) C6 83 BF A0 | mov filterCutoff, filterControl '|
0188(005C) 10 8C FF 28 | shr filterControl, #16 '| Filter control
018C(005D) 05 82 FF 28 | shr filterCutoff, #5 '|
0190(005E) 07 82 FF 64 | andn filterCutoff, #7 '|
0194(005F) C6 95 BF A0 | mov tempValue, filterControl '|
0198(0060) 07 94 FF 60 | and tempValue, #7 '| Filter cutoff frequency
019C(0061) CA 83 BF 68 | or filterCutoff, tempValue '|
01A0(0062) 84 83 BF 60 | and filterCutoff, mask11bit '|
01A4(0063) 8C 83 BF 80 | add filterCutoff, filterOffset '|
01A8(0064) C6 8B BF A0 | mov filterMode_Volume, filterControl '| Main volume and filter mode
01AC(0065) 08 8A FF 28 | shr filterMode_Volume, #8 '|
01B0(0066) C6 81 BF A0 | mov filterResonance,filterControl '|
01B4(0067) F0 80 FF 60 | and filterResonance,#$F0 '| Filter Resonance level
01B8(0068) 04 80 FF 28 | shr filterResonance,#4 '|
01BC(0069) A2 4B BF 81 | SID add phaseAccumulator1, frequency1 wc ' Add frequency value to phase accumulator 1
01C0(006A) 02 40 CF 64 | if_nc andn controlRegister2, #2
01C4(006B) 0A 40 7F 62 | test controlRegister2, #10 wz ' Sync oscilator 2 to oscillator 1 if sync = on
01C8(006C) 00 4C D7 A0 | if_nz mov phaseAccumulator2, #0 ' Or reset counter 2 when bit 4 of control register is 1
01CC(006D) A3 4D BF 81 | add phaseAccumulator2, frequency2 wc
01D0(006E) 02 42 CF 64 | if_nc andn controlRegister3, #2
01D4(006F) 0A 42 7F 62 | test controlRegister3, #10 wz ' Sync oscilator 3 to oscillator 2 if sync = on
01D8(0070) 00 4E D7 A0 | if_nz mov phaseAccumulator3, #0 ' Or reset oscilator 3 when bit 4 of control register is 1
01DC(0071) A4 4F BF 81 | add phaseAccumulator3, frequency3 wc
01E0(0072) 02 3E CF 64 | if_nc andn controlRegister1, #2
01E4(0073) 0A 3E 7F 62 | test controlRegister1, #10 wz ' Sync oscilator 1 to oscillator 3 if sync = on
01E8(0074) 00 4A D7 A0 | if_nz mov phaseAccumulator1, #0 ' Or reset oscilator 1 when bit 4 of control register is 1
01EC(0075) 02 56 7F 86 | Saw1 cmp selectedWaveform1, #2 wz
01F0(0076) A5 23 BF A0 | mov arg1, phaseAccumulator1
01F4(0077) 99 00 68 5C | if_z jmp #Envelope1
01F8(0078) 01 56 7F 87 | Triangle1 cmp selectedWaveform1, #1 wz, wc
01FC(0079) 80 00 54 5C | if_nz jmp #Square1
0200(007A) 01 22 FF 2D | shl arg1, #1 wc
0204(007B) 7E 23 B3 6C | if_c xor arg1, mask32bit
0208(007C) 04 3E 7F 62 | test controlRegister1, #4 wz '|
020C(007D) 85 4F 17 62 | if_nz test phaseAccumulator3, val31bit wz '| These 3 lines handles ring modulation
0210(007E) 7E 23 97 6C | if_nz xor arg1, mask32bit '|
0214(007F) 99 00 7C 5C | jmp #Envelope1
0218(0080) 04 56 7F 86 | Square1 cmp selectedWaveform1, #4 wz
021C(0081) A5 51 AB 85 | if_z sub pulseWidth1, phaseAccumulator1 wc ' C holds the pulse width modulated square wave
0220(0082) 7E 23 AB 70 | if_z muxc arg1, mask32bit
0224(0083) 99 00 68 5C | if_z jmp #Envelope1
0228(0084) 08 56 7F 86 | Noise1 cmp selectedWaveform1, #8 wz
022C(0085) 8E 00 54 5C | if_nz jmp #Combined1
0230(0086) 80 23 BF 60 | and arg1, mask28bit
0234(0087) A2 23 BF 85 | sub arg1, frequency1 wc
0238(0088) 96 23 BF 58 | movi arg1, noiseValue1
023C(0089) 94 23 BF 80 | add arg1, noiseAddValue
0240(008A) 99 00 4C 5C | if_nc jmp #Envelope1
0244(008B) 95 2D 3F 61 | test noiseValue1, noiseTap wc
0248(008C) 01 2C FF 30 | rcr noiseValue1, #1
024C(008D) 99 00 7C 5C | jmp #Envelope1
0250(008E) 08 56 7F 62 | Combined1 test selectedWaveform1, #8 wz
0254(008F) 04 56 FF 84 | sub selectedWaveform1, #4
0258(0090) 00 56 FF 40 | mins selectedWaveform1, #0
025C(0091) 08 56 FF 2C | shl selectedWaveform1, #8
0260(0092) A5 95 BF A0 | mov tempValue, phaseAccumulator1
0264(0093) 18 94 FF 28 | shr tempValue, #24
0268(0094) CA 57 BF 80 | add selectedWaveform1, tempValue
026C(0095) 90 57 BF 80 | add selectedWaveform1, combTableAddr
0270(0096) AB 23 8B 00 | if_nc_and_z rdbyte arg1, selectedWaveform1
0274(0097) 18 22 CB 2C | if_nc_and_z shl arg1, #24
0278(0098) 85 23 B7 A0 | if_c_or_nz mov arg1, val31bit
027C(0099) 8D 95 BF A0 | Envelope1 mov tempValue, decayDivideRef
0280(009A) 99 95 BF 28 | shr tempValue, decayDivide1
0284(009B) CA 39 3F 85 | cmp envelopeLevel1, tempValue wc
0288(009C) A2 5C 7F E8 | tjnz envelopeState1, #Env_Dec1 nr
028C(009D) 01 32 CF E0 | Env_At1 if_nc cmpsub decayDivide1, #1
0290(009E) B1 39 BF 81 | add envelopeLevel1, attack1 wc
0294(009F) 7E 39 B3 A0 | if_c mov envelopeLevel1, mask32bit
0298(00A0) 01 5C F3 A0 | if_c mov envelopeState1, #1
029C(00A1) AC 00 7C 5C | jmp #Amplitude1
02A0(00A2) 01 32 F3 80 | Env_Dec1 if_c add decayDivide1, #1
02A4(00A3) 9C F9 3E 85 | cmp startLogLevel, envelopeLevel1 wc
02A8(00A4) 01 5C 7F 86 | cmp envelopeState1, #1 wz
02AC(00A5) AA 00 54 5C | if_nz jmp #Rel1
02B0(00A6) 99 69 8F 28 | if_nc shr decay1, decayDivide1
02B4(00A7) B4 39 BF 84 | sub envelopeLevel1, decay1
02B8(00A8) B7 39 BF 49 | min envelopeLevel1, sustain1 wc
02BC(00A9) AC 00 7C 5C | jmp #Amplitude1
02C0(00AA) 99 75 8F 28 | Rel1 if_nc shr release1, decayDivide1
02C4(00AB) BA 39 BF E0 | cmpsub envelopeLevel1, release1
02C8(00AC) 0E 22 FF 28 | Amplitude1 shr arg1, #14
02CC(00AD) 88 23 BF 84 | sub arg1, val17bit
02D0(00AE) 9C 25 BF A0 | mov arg2, envelopeLevel1
02D4(00AF) 18 24 FF 28 | shr arg2, #24
02D8(00B0) 66 D7 FE 5C | call #multiply
02DC(00B1) 93 7B BF A0 | mov out1, r1
02E0(00B2) 02 58 7F 86 | Saw2 cmp selectedWaveform2, #2 wz
02E4(00B3) A6 23 BF A0 | mov arg1, phaseAccumulator2
02E8(00B4) D6 00 68 5C | if_z jmp #Envelope2
02EC(00B5) 01 58 7F 87 | Triangle2 cmp selectedWaveform2, #1 wz, wc
02F0(00B6) BD 00 54 5C | if_nz jmp #Square2
02F4(00B7) 01 22 FF 2D | shl arg1, #1 wc
02F8(00B8) 7E 23 B3 6C | if_c xor arg1, mask32bit
02FC(00B9) 04 40 7F 62 | test controlRegister2, #4 wz '|
0300(00BA) 85 4B 17 62 | if_nz test phaseAccumulator1, val31bit wz '| These 3 lines handles ring modulation
0304(00BB) 7E 23 97 6C | if_nz xor arg1, mask32bit '|
0308(00BC) D6 00 7C 5C | jmp #Envelope2
030C(00BD) 04 58 7F 86 | Square2 cmp selectedWaveform2, #4 wz
0310(00BE) A6 53 AB 85 | if_z sub pulseWidth2, phaseAccumulator2 wc ' C holds the pulse width modulated square wave
0314(00BF) 7E 23 AB 70 | if_z muxc arg1, mask32bit
0318(00C0) D6 00 68 5C | if_z jmp #Envelope2
031C(00C1) 08 58 7F 86 | Noise2 cmp selectedWaveform2, #8 wz
0320(00C2) CB 00 54 5C | if_nz jmp #Combined2
0324(00C3) 80 23 BF 60 | and arg1, mask28bit
0328(00C4) A3 23 BF 85 | sub arg1, frequency2 wc
032C(00C5) 97 23 BF 58 | movi arg1, noiseValue2
0330(00C6) 94 23 BF 80 | add arg1, noiseAddValue
0334(00C7) D6 00 4C 5C | if_nc jmp #Envelope2
0338(00C8) 95 2F 3F 61 | test noiseValue2, noiseTap wc
033C(00C9) 01 2E FF 30 | rcr noiseValue2, #1
0340(00CA) D6 00 7C 5C | jmp #Envelope2
0344(00CB) 08 58 7F 62 | Combined2 test selectedWaveform2, #8 wz
0348(00CC) 04 58 FF 84 | sub selectedWaveform2, #4
034C(00CD) 00 58 FF 40 | mins selectedWaveform2, #0
0350(00CE) 08 58 FF 2C | shl selectedWaveform2, #8
0354(00CF) A6 95 BF A0 | mov tempValue, phaseAccumulator2
0358(00D0) 18 94 FF 28 | shr tempValue, #24
035C(00D1) CA 59 BF 80 | add selectedWaveform2, tempValue
0360(00D2) 90 59 BF 80 | add selectedWaveform2, combTableAddr
0364(00D3) AC 23 8B 00 | if_nc_and_z rdbyte arg1, selectedWaveform2
0368(00D4) 18 22 CB 2C | if_nc_and_z shl arg1, #24
036C(00D5) 85 23 B7 A0 | if_c_or_nz mov arg1, val31bit
0370(00D6) 8D 95 BF A0 | Envelope2 mov tempValue, decayDivideRef
0374(00D7) 9A 95 BF 28 | shr tempValue, decayDivide2
0378(00D8) CA 3B 3F 85 | cmp envelopeLevel2, tempValue wc
037C(00D9) DF 5E 7F E8 | tjnz envelopeState2, #Env_Dec2 nr
0380(00DA) 01 34 CF E0 | Env_At2 if_nc cmpsub decayDivide2, #1
0384(00DB) B2 3B BF 81 | add envelopeLevel2, attack2 wc
0388(00DC) 7E 3B B3 A0 | if_c mov envelopeLevel2, mask32bit
038C(00DD) 01 5E F3 A0 | if_c mov envelopeState2, #1
0390(00DE) E9 00 7C 5C | jmp #Amplitude2
0394(00DF) 01 34 F3 80 | Env_Dec2 if_c add decayDivide2, #1
0398(00E0) 9D F9 3E 85 | cmp startLogLevel,envelopeLevel2 wc
039C(00E1) 01 5E 7F 86 | cmp envelopeState2, #1 wz
03A0(00E2) E7 00 54 5C | if_nz jmp #Rel2
03A4(00E3) 9A 6B 8F 28 | if_nc shr decay2, decayDivide2
03A8(00E4) B5 3B BF 84 | sub envelopeLevel2, decay2
03AC(00E5) B8 3B BF 49 | min envelopeLevel2, sustain2 wc
03B0(00E6) E9 00 7C 5C | jmp #Amplitude2
03B4(00E7) 9A 77 8F 28 | Rel2 if_nc shr release2, decayDivide2
03B8(00E8) BB 3B BF E0 | cmpsub envelopeLevel2, release2
03BC(00E9) 0E 22 FF 28 | Amplitude2 shr arg1, #14
03C0(00EA) 88 23 BF 84 | sub arg1, val17bit
03C4(00EB) 9D 25 BF A0 | mov arg2, envelopeLevel2
03C8(00EC) 18 24 FF 28 | shr arg2, #24
03CC(00ED) 66 D7 FE 5C | call #multiply
03D0(00EE) 93 7D BF A0 | mov out2, r1
03D4(00EF) 02 5A 7F 86 | Saw3 cmp selectedWaveform3, #2 wz
03D8(00F0) A7 23 BF A0 | mov arg1, phaseAccumulator3
03DC(00F1) 13 01 68 5C | if_z jmp #Envelope3
03E0(00F2) 01 5A 7F 87 | Triangle3 cmp selectedWaveform3, #1 wz, wc
03E4(00F3) FA 00 54 5C | if_nz jmp #Square3
03E8(00F4) 01 22 FF 2D | shl arg1, #1 wc
03EC(00F5) 7E 23 B3 6C | if_c xor arg1, mask32bit
03F0(00F6) 04 42 7F 62 | test controlRegister3, #4 wz '|
03F4(00F7) 85 4D 17 62 | if_nz test phaseAccumulator2, val31bit wz '| These 3 lines handles ring modulation
03F8(00F8) 7E 23 97 6C | if_nz xor arg1, mask32bit '|
03FC(00F9) 13 01 7C 5C | jmp #Envelope3
0400(00FA) 04 5A 7F 86 | Square3 cmp selectedWaveform3, #4 wz
0404(00FB) A7 55 AB 85 | if_z sub pulseWidth3, phaseAccumulator3 wc ' C holds the pulse width modulated square wave
0408(00FC) 7E 23 AB 70 | if_z muxc arg1, mask32bit
040C(00FD) 13 01 68 5C | if_z jmp #Envelope3
0410(00FE) 08 5A 7F 86 | Noise3 cmp selectedWaveform3, #8 wz
0414(00FF) 08 01 54 5C | if_nz jmp #Combined3
0418(0100) 80 23 BF 60 | and arg1, mask28bit
041C(0101) A4 23 BF 85 | sub arg1, frequency3 wc
0420(0102) 98 23 BF 58 | movi arg1, noiseValue3
0424(0103) 94 23 BF 80 | add arg1, noiseAddValue
0428(0104) 13 01 4C 5C | if_nc jmp #Envelope3
042C(0105) 95 31 3F 61 | test noiseValue3, noiseTap wc
0430(0106) 01 30 FF 30 | rcr noiseValue3, #1
0434(0107) 13 01 7C 5C | jmp #Envelope3
0438(0108) 08 5A 7F 62 | Combined3 test selectedWaveform3, #8 wz
043C(0109) 04 5A FF 84 | sub selectedWaveform3, #4
0440(010A) 00 5A FF 40 | mins selectedWaveform3, #0
0444(010B) 08 5A FF 2C | shl selectedWaveform3, #8
0448(010C) A7 95 BF A0 | mov tempValue, phaseAccumulator3
044C(010D) 18 94 FF 28 | shr tempValue, #24
0450(010E) CA 5B BF 80 | add selectedWaveform3, tempValue
0454(010F) 90 5B BF 80 | add selectedWaveform3, combTableAddr
0458(0110) AD 23 8B 00 | if_nc_and_z rdbyte arg1, selectedWaveform3
045C(0111) 18 22 CB 2C | if_nc_and_z shl arg1, #24
0460(0112) 85 23 B7 A0 | if_c_or_nz mov arg1, val31bit
0464(0113) 8D 95 BF A0 | Envelope3 mov tempValue, decayDivideRef
0468(0114) 9B 95 BF 28 | shr tempValue, decayDivide3
046C(0115) CA 3D 3F 85 | cmp envelopeLevel3, tempValue wc
0470(0116) 1C 61 7F E8 | tjnz envelopeState3, #Env_Dec3 nr
0474(0117) 01 36 CF E0 | Env_At3 if_nc cmpsub decayDivide3, #1
0478(0118) B3 3D BF 81 | add envelopeLevel3, attack3 wc
047C(0119) 7E 3D B3 A0 | if_c mov envelopeLevel3, mask32bit
0480(011A) 01 60 F3 A0 | if_c mov envelopeState3, #1
0484(011B) 26 01 7C 5C | jmp #Amplitude3
0488(011C) 01 36 F3 80 | Env_Dec3 if_c add decayDivide3, #1
048C(011D) 9E F9 3E 85 | cmp startLogLevel, envelopeLevel3 wc
0490(011E) 01 60 7F 86 | cmp envelopeState3, #1 wz
0494(011F) 24 01 54 5C | if_nz jmp #Rel3
0498(0120) 9B 6D 8F 28 | if_nc shr decay3, decayDivide3
049C(0121) B6 3D BF 84 | sub envelopeLevel3, decay3
04A0(0122) B9 3D BF 49 | min envelopeLevel3, sustain3 wc
04A4(0123) 26 01 7C 5C | jmp #Amplitude3
04A8(0124) 9B 79 8F 28 | Rel3 if_nc shr release3, decayDivide3
04AC(0125) BC 3D BF E0 | cmpsub envelopeLevel3, release3
04B0(0126) 0E 22 FF 28 | Amplitude3 shr arg1, #14
04B4(0127) 88 23 BF 84 | sub arg1, val17bit
04B8(0128) 9E 25 BF A0 | mov arg2, envelopeLevel3
04BC(0129) 18 24 FF 28 | shr arg2, #24
04C0(012A) 66 D7 FE 5C | call #multiply
04C4(012B) 93 7F BF A0 | mov out3, r1
04C8(012C) 00 90 FF A0 | filter mov ordinaryOutput, #0 '|
04CC(012D) 00 84 FF A0 | mov highPassFilter, #0 '|
04D0(012E) 01 8C 7F 61 | test filterControl, #1 wc '|
04D4(012F) BD 85 B3 80 | if_c add highPassFilter, out1 '|
04D8(0130) BD 91 8F 80 | if_nc add ordinaryOutput, out1 '|
04DC(0131) 02 8C 7F 61 | test filterControl, #2 wc '| Route channels trough the filter
04E0(0132) BE 85 B3 80 | if_c add highPassFilter, out2 '| or bypass them
04E4(0133) BE 91 8F 80 | if_nc add ordinaryOutput, out2 '|
04E8(0134) 04 8C 7F 61 | test filterControl, #4 wc '|
04EC(0135) BF 85 B3 80 | if_c add highPassFilter, out3 '|
04F0(0136) BF 91 8F 80 | if_nc add ordinaryOutput, out3 '|
04F4(0137) C0 25 BF A0 | mov arg2, filterResonance '|
04F8(0138) 06 24 FF 80 | add arg2, #RESONANCE_OFFSET '|
04FC(0139) C3 23 BF A0 | mov arg1, bandPassFilter '|
0500(013A) 05 22 FF 38 | sar arg1, #RESONANCE_FACTOR '|
0504(013B) 66 D7 FE 5C | call #multiply '| High pass filter
0508(013C) C3 85 BF 84 | sub highPassFilter, bandPassFilter '|
050C(013D) 93 85 BF 80 | add highPassFilter, r1 '|
0510(013E) C4 85 BF 84 | sub highPassFilter, lowPassFilter '|
0514(013F) C2 23 BF A0 | mov arg1, highPassFilter '|
0518(0140) 0A 22 FF 38 | sar arg1, #BP_MAX_CUTOFF '|
051C(0141) C1 25 BF A0 | mov arg2, filterCutoff '| Band pass filter
0520(0142) 8E 25 BF 4C | max arg2, maxCutoff '|
0524(0143) 66 D7 FE 5C | call #multiply '|
0528(0144) 93 87 BF 80 | add bandPassFilter, r1 '|
052C(0145) C3 23 BF A0 | mov arg1, bandPassFilter '|
0530(0146) 0B 22 FF 38 | sar arg1, #LP_MAX_CUTOFF '|
0534(0147) C1 25 BF A0 | mov arg2, filterCutoff '| Low pass filter
0538(0148) 66 D7 FE 5C | call #multiply '|
053C(0149) 93 89 BF 80 | add lowPassFilter, r1 '|
0540(014A) 00 8E FF A0 | mov filterOutput, #0 '|
0544(014B) 10 8A 7F 61 | test filterMode_Volume, #16 wc '|
0548(014C) C4 8F B3 80 | if_c add filterOutput, lowPassFilter '|
054C(014D) 20 8A 7F 61 | test filterMode_Volume, #32 wc '| Enable/Disable
0550(014E) C3 8F B3 80 | if_c add filterOutput, bandPassFilter '| Low/Band/High pass filtering
0554(014F) 40 8A 7F 61 | test filterMode_Volume, #64 wc '|
0558(0150) C2 8F B3 80 | if_c add filterOutput, highPassFilter '|
055C(0151) C7 23 BF A0 | mixer mov arg1, filterOutput
0560(0152) C8 23 BF 80 | add arg1, ordinaryOutput
0564(0153) 8A 23 BF 44 | maxs arg1, clipLevelHigh '|
0568(0154) 8B 23 BF 40 | mins arg1, clipLevelLow '|
056C(0155) C5 25 BF A0 | mov arg2, filterMode_Volume '| Main volume adjustment
0570(0156) 0F 24 FF 60 | and arg2, #15 '|
0574(0157) 66 D7 FE 5C | call #multiply '|
0578(0158) 85 27 BF 80 | add r1, val31bit ' DC offset
057C(0159) 8F 93 BF F8 | waitcnt waitCounter, sampleRate ' Wait until the right time to update
0580(015A) 93 F5 BF A0 | mov FRQA, r1 '| Update PWM values in FRQA/FRQB
0584(015B) 93 F7 BF A0 | mov FRQB, r1 '|
0588(015C) F0 95 BF A0 | mov tempValue, par
058C(015D) 1C 94 FF 80 | add tempValue, #28
0590(015E) CA 27 3F 08 | wrlong r1, tempValue '| Write the sample to hub ram
0594(015F) 05 00 7C 5C | jmp #getRegisters
0598(0160) 91 C9 BE 50 | getADSR movs :indexed1, arg1
059C(0161) F0 C9 FE 64 | andn :indexed1, #$1F0
05A0(0162) 6C C9 FE 80 | add :indexed1, #ADSRTable
05A4(0163) 04 22 FF 28 | shr arg1, #4
05A8(0164) 00 26 BF A0 | :indexed1 mov r1, 0
05AC(0165) 00 00 7C 5C | getADSR_ret ret
05B0(0166) 00 26 FF A0 | multiply mov r1, #0 'Clear 32-bit product
05B4(0167) 01 24 FF 2B | :multiLoop shr arg2, #1 wc, wz 'Half multiplyer and get LSB of it
05B8(0168) 91 27 B3 80 | if_c add r1, arg1 'Add multiplicand to product on C
05BC(0169) 01 22 FF 2C | shl arg1, #1 'Double multiplicand
05C0(016A) 67 01 54 5C | if_nz jmp #:multiLoop 'Check nonzero multiplier to continue multiplication
05C4(016B) 00 00 7C 5C | multiply_ret ret
05C8(016C) 38 07 9C 03 | ADSRTable long trunc(ENV_CAL_FACTOR * (1.0 / 9.0 )) '2 ms
05CC(016D) 08 E2 03 01 | long trunc(ENV_CAL_FACTOR * (1.0 / 32.0 )) '8 ms
05D0(016E) 09 01 84 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 63.0 )) '16 ms
05D4(016F) 1E 8A 57 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 95.0 )) '24 ms
05D8(0170) 54 D0 37 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 149.0 )) '38 ms
05DC(0171) 18 CD 25 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 220.0 )) '56 ms
05E0(0172) A3 25 1F 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 267.0 )) '68 ms
05E4(0173) CA 91 1A 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 313.0 )) '80 ms
05E8(0174) 05 37 15 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 392.0 )) '100 ms
05EC(0175) 14 83 08 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 977.0 )) '250 ms
05F0(0176) 8A 41 04 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 1954.0 )) '500 ms
05F4(0177) 0C A9 02 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 3126.0 )) '800 ms
05F8(0178) E8 20 02 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 3907.0 )) '1 s
05FC(0179) A6 B5 00 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 11720.0)) '3 s
0600(017A) FF 6C 00 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 19532.0)) '5 s
0604(017B) 1F 44 00 00 | long trunc(ENV_CAL_FACTOR * (1.0 / 31251.0)) '8 s
0608(017C) 5D 5D 5D 5D | startLogLevel long START_LOG_LEVEL
060C(017D) 00 00 00 0F | sustainAdd long $0f000000
0610(017E) FF FF FF FF | mask32bit long $ffffffff
0614(017F) FF FF FF 7F | mask31bit long $7fffffff
0618(0180) FF FF FF 0F | mask28bit long $fffffff
061C(0181) FF FF FF 00 | mask24bit long $ffffff
0620(0182) FF FF 0F 00 | mask20bit long $fffff
0624(0183) FF FF 00 00 | mask16bit long $ffff
0628(0184) FF 07 00 00 | mask11bit long $7ff
062C(0185) 00 00 00 80 | val31bit long $80000000
0630(0186) 00 00 00 10 | val28bit long $10000000
0634(0187) 00 00 00 08 | val27bit long $8000000
0638(0188) 00 00 02 00 | val17bit long $20000
063C(0189) 00 00 01 00 | val16bit long $10000
0640(018A) 00 00 00 08 | clipLevelHigh long $8000000
0644(018B) 00 00 00 F8 | clipLevelLow long-$8000000
0648(018C) 0C 00 00 00 | filterOffset long FILTER_OFFSET
064C(018D) 6C 6C 6C 6C | decayDivideRef long DECAY_DIVIDE_REF
0650(018E) 4C 04 00 00 | maxCutoff long CUTOFF_LIMIT
0654(018F) 00 00 00 00 | sampleRate long 0 'clocks between samples ( ~31.250 khz )
0658(0190) 00 00 00 00 | combTableAddr long 0
065C(0191) 01 00 00 00 | arg1 long 1
0660(0192) 01 00 00 00 | arg2 long 1
0664(0193) 01 00 00 00 | r1 long 1
0668(0194) 00 00 80 AA | noiseAddValue long NOISE_ADD
066C(0195) 00 21 00 00 | noiseTap long NOISE_TAP
0670(0196) FF FF FF 00 | noiseValue1 long $ffffff
0674(0197) FF FF FF 00 | noiseValue2 long $ffffff
0678(0198) FF FF FF 00 | noiseValue3 long $ffffff
067C(0199) 00 00 00 00 | decayDivide1 long 0
0680(019A) 00 00 00 00 | decayDivide2 long 0
0684(019B) 00 00 00 00 | decayDivide3 long 0
{
0688(019C) | envelopeLevel1 res 1
0688(019D) | envelopeLevel2 res 1
0688(019E) | envelopeLevel3 res 1
0688(019F) | controlRegister1 res 1
0688(01A0) | controlRegister2 res 1
0688(01A1) | controlRegister3 res 1
0688(01A2) | frequency1 res 1
0688(01A3) | frequency2 res 1
0688(01A4) | frequency3 res 1
0688(01A5) | phaseAccumulator1 res 1
0688(01A6) | phaseAccumulator2 res 1
0688(01A7) | phaseAccumulator3 res 1
0688(01A8) | pulseWidth1 res 1
0688(01A9) | pulseWidth2 res 1
0688(01AA) | pulseWidth3 res 1
0688(01AB) | selectedWaveform1 res 1
0688(01AC) | selectedWaveform2 res 1
0688(01AD) | selectedWaveform3 res 1
0688(01AE) | envelopeState1 res 1
0688(01AF) | envelopeState2 res 1
0688(01B0) | envelopeState3 res 1
0688(01B1) | attack1 res 1
0688(01B2) | attack2 res 1
0688(01B3) | attack3 res 1
0688(01B4) | decay1 res 1
0688(01B5) | decay2 res 1
0688(01B6) | decay3 res 1
0688(01B7) | sustain1 res 1
0688(01B8) | sustain2 res 1
0688(01B9) | sustain3 res 1
0688(01BA) | release1 res 1
0688(01BB) | release2 res 1
0688(01BC) | release3 res 1
0688(01BD) | out1 res 1
0688(01BE) | out2 res 1
0688(01BF) | out3 res 1
0688(01C0) | filterResonance res 1
0688(01C1) | filterCutoff res 1
0688(01C2) | highPassFilter res 1
0688(01C3) | bandPassFilter res 1
0688(01C4) | lowPassFilter res 1
0688(01C5) | filterMode_Volume res 1
0688(01C6) | filterControl res 1
0688(01C7) | filterOutput res 1
0688(01C8) | ordinaryOutput res 1
0688(01C9) | waitCounter res 1
0688(01CA) | tempValue res 1
}
unum ~ \ restore number exception processing
}
]~
END