{{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ AYcog - AY-3-891X / YM2149 emulator V0.22 (C) 2010-05 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. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }} CON PSG_FREQ = 2_000_000.0 ' Clock frequency input to the chip (Colour Genie EG2000 computer runs at 2.2Mhz) ' WARNING !! ' Don't alter the constants below unless you know what you are doing '------------------------------------------------------------------- SAMPLE_RATE = 125_000 ' Sample rate of AYcog OSC_CORR = trunc(1.05 * PSG_FREQ) ' Calibrates the relative oscillator frequency NOISE_CORR = OSC_CORR>>1 ' Calibrates the relative noise frequency ENV_CORR = OSC_CORR>>6 ' Calibrates the relative envelope timing ' ' Reg bits function ' ----------------------------------- ' 00 7..0 channel A fine tune ' 01 3..0 channel A coarse tune ' 02 7..0 channel B fine tune ' 03 3..0 channel B coarse tune ' 04 7..0 channel C fine tune ' 05 3..0 channel C coarse tune ' 06 4..0 noise period ' 07 7..0 enable register ' 08 4..0 channel A volume ' 09 4..0 channel B volume ' 10 4..0 channel C volume ' 11 7..0 envelope fine tune ' 12 7..0 envelope coarse tune ' 13 3..0 envelope shape ' 14 7..0 I/O port A value ' 15 7..0 I/O port B value VAR long cog PUB start(right,left,AYregisters) if (AYregisters & 1) <> 0 ' we need word aligned registers abort(-1) arg1 := $18000000 | left arg2 := $18000000 | right r1 := ((1< envelopeAmplitude '─────────────────────────────────────────────────────────── Envelope sub envCounter, envSubValue wc ' Handles envelope incrementing if_c add envCounter, envelopePeriod if_c add envelopeValue, envelopeInc '─────────────────────────────────────────────────────────── test envelopeShape, #16 wz ' Handle envelope reset bit ( Extra bit added by Ahle2 ) if_z neg envelopeValue, #0 if_z mov envelopeInc, #1 if_z mov envCounter, envelopePeriod if_z or envelopeShape, #16 if_z wrbyte envelopeShape, tempValue '<-IMPORTANT, sets bit 5 in hub ram '─────────────────────────────────────────────────────────── test envelopeShape, #8 wc ' Handle continue = 0 test envelopeShape, #4 wz if_nc_and_z mov envelopeShape, #9 if_nc_and_nz mov envelopeShape, #15 '─────────────────────────────────────────────────────────── test envelopeShape, #2 wz ' Sets the envelope hold level muxz envHoldLevel, #15 ' '─────────────────────────────────────────────────────────── test envelopeValue, #16 wz ' Check if > 15 test envelopeShape, #1 wc ' Check hold bit if_nz_and_c mov envelopeInc, #0 ' Hold envelope if_nz_and_c mov envelopeValue, envHoldLevel ' '─────────────────────────────────────────────────────────── if_nz test envelopeShape, #2 wc ' Check and handle alternation if_nz_and_c neg envelopeInc, envelopeInc if_nz_and_c add envelopeValue, envelopeInc '─────────────────────────────────────────────────────────── mov envelopeAmplitude, envelopeValue test envelopeShape, #4 wc ' Check and handle invertion (attack) if_nc xor envelopeAmplitude, #15 '(Move Value or ~Value to envelopeAmplitude) '─────────────────────────────────────────────────────────── ' Waveform shaping noise -> bit 3 of oscValues '─────────────────────────────────────────────────────────── Noise1 sub phaseAccumulatorN, noiseSubValue wc ' Noise generator if_c add phaseAccumulatorN, noisePeriod if_c add noiseValue, noiseAdd if_c ror noiseValue, #15 wc if_c xor oscValues, #8 '─────────────────────────────────────────────────────────── ' Waveform shaping channel 1 -> out1 '─────────────────────────────────────────────────────────── Env1 test amplitude1, #16 wz ' Selects envelope or fixed amplitude if_nz mov amplitude1, envelopeAmplitude ' depending on bit 5 of amplitude register 1 mov arg1, amplitude1 call #getAmplitude '─────────────────────────────────────────────────────────── Square1 cmp frequency1, freqRef wc if_nc sub phaseAccumulator1, oscSubValue wc ' Square wave generator if_c add phaseAccumulator1, frequency1 ' channel 1 if_c xor oscValues, #1 '─────────────────────────────────────────────────────────── test oscValues, mask513 wz ' Handles mixing of channel 1 negnz out1, r1 ' Tone on/off, Noice on/off test oscValues, mask4104 wz if_z mov out1, r1 ' arg2 = (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable) '─────────────────────────────────────────────────────────── ' Waveform shaping channel 2 -> out2 '─────────────────────────────────────────────────────────── Env2 test amplitude2, #16 wz ' Selects envelope or fixed amplitude if_nz mov amplitude2, envelopeAmplitude ' depending on bit 5 of amplitude register 2 mov arg1, amplitude2 call #getAmplitude '─────────────────────────────────────────────────────────── Square2 cmp frequency2, freqRef wc if_nc sub phaseAccumulator2, oscSubValue wc ' Square wave generator if_c add phaseAccumulator2, frequency2 ' channel 2 if_c xor oscValues, #2 '─────────────────────────────────────────────────────────── test oscValues, mask1026 wz ' Handles mixing of channel 2 negz out2, r1 ' Tone on/off, Noice on/off test oscValues, mask8200 wz if_z mov out2, r1 ' arg2 = (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable) '─────────────────────────────────────────────────────────── ' Waveform shaping channel 3 -> out3 '─────────────────────────────────────────────────────────── Env3 test amplitude3, #16 wz ' Selects envelope or fixed amplitude if_nz mov amplitude3, envelopeAmplitude ' depending on bit 5 of amplitude register 3 mov arg1, amplitude3 call #getAmplitude '─────────────────────────────────────────────────────────── Square3 cmp frequency3, freqRef wc if_nc sub phaseAccumulator3, oscSubValue wc ' Square wave generator if_c add phaseAccumulator3, frequency3 ' channel 3 if_c xor oscValues, #4 '─────────────────────────────────────────────────────────── test oscValues, mask2052 wz ' Handles mixing of channel 2 negz out3, r1 ' Tone on/off, Noice on/off test oscValues, mask16392 wz if_z mov out3, r1 ' arg2 = (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable) AY_ret ret ' ' Mix channels and update FRQA/FRQB PWM-values ' mixer mov r1, val31bit ' DC offset add r1, out1 add r1, out2 add r1, out3 waitcnt waitCounter, sampleRate ' Wait until the right time to update mov FRQA, r1 '| Update PWM values in FRQA/FRQB mov FRQB, r1 '| mixer_ret ret ' ' Get amplitude table r1 = amplitudTable[arg1] ' getAmplitude and arg1, #15 add arg1, #amplitudeTable ' Lookup the amplitude according movs :indexed1, arg1 ' to the current state of the envelope nop :indexed1 mov r1, 0 getAmplitude_ret ret ' ' Variables, tables, masks and reference values ' amplitudeTable long 1634706 long 2452059 long 3678089 long 5517133 long 8275700 long 12413550 long 18620325 long 27930488 long 41895733 long 62843600 long 94265400 long 141398100 long 212097150 long 318145725 long 477218588 long 715827882 'Masks and reference values mask513 long 513 mask1026 long 1026 mask2052 long 2052 mask4104 long 4104 mask8200 long 8200 mask16392 long 16392 mask32bit long $ffffffff mask16bit long $ffff half_period long $00008000 val31bit long $80000000 noiseAdd long $88008800 'Value to add to the noise generator every noise update sampleRate long 0 freqRef long 10<<20 'Setup and subroutine parameters arg1 long 0 arg2 long 0 r1 long 0 AY_Address long 0 'AY variables envCounter long 1 envSubValue long ENV_CORR oscSubValue long OSC_CORR noiseSubValue long NOISE_CORR envelopeValue long 0 envelopeInc long 1 envHoldLevel res 1 oscValues res 1 amplitude1 res 1 amplitude2 res 1 amplitude3 res 1 envelopeAmplitude res 1 enableRegister res 1 envelopeShape res 1 frequency1 res 1 frequency2 res 1 frequency3 res 1 envelopePeriod res 1 noisePeriod res 1 phaseAccumulatorN res 1 phaseAccumulator1 res 1 phaseAccumulator2 res 1 phaseAccumulator3 res 1 noiseValue res 1 noiseOut res 1 out1 res 1 out2 res 1 out3 res 1 waitCounter res 1 tempValue res 1 temp1 res 1 fit