User Tools

Site Tools


base:spectrometer

This is the code from T.P.C.T.S. demo that calculates the index values for the spectrometer. There are a few tricks in there, but I am sure many people could have written a more optimized version.

I hope it will help you understand how I did it.

Code can be compiled using KickAss.

/Trap

.const SID_Ghostbytes = $40                                     // Location of SID Ghostbytes (16 bytes)

/////////////////////////////////////////////////////////////////////////////////
// Spectrometer
/////////////////////////////////////////////////////////////////////////////////

Spectrometer:               // Call this every frame. You can then use the values in the dBMeterValue table as index for drawing your spectrometer bands.

DecreaseBarIndexes:         ldx #16                             // MeterTemp contains the index values, or height, of each of the 16 bands.
!:                          lda MeterTemp,x                     // This code will go through each of these values and decrease them until = 0.
                            beq dBMeterUpdate_NoDec    
                            dec MeterTemp,x
dBMeterUpdate_NoDec:        tay
                            lda SoundbarSine,y                  // The index value from MeterTemp is used to get a value from a bouncing sinewave.
                            sta dBMeterValue,x                  // SoundbarSine is the sine, it contains a 90 degree drop and a small 180 degree bounce.
                            dex
                            bpl !-

                            ldx SID_Ghostbytes                  // Get Channel 1 Note
                            lda SID_Ghostbytes+1
                            jsr GetNote
                            lda SID_Ghostbytes+6                // Isolate sustain value
                            and #$f0
                            lsr
                            lsr
                            lsr
                            lsr
                            clc
                            adc #6
                            sta MeterTemp,x                     // Store new index for band representing the node played. X was received by GetNote function.
                            jsr CalculateSurroundings           // Calculate polarization of neighbouring bands

                            ldx SID_Ghostbytes+7                // Channel 2 Note
                            lda SID_Ghostbytes+8
                            jsr GetNote
                            lda SID_Ghostbytes+$d               // Repeat channel 2
                            and #$f0
                            lsr
                            lsr           
                            lsr           
                            lsr
                            clc
                            adc #6
                            sta MeterTemp,x
                            jsr CalculateSurroundings

                            ldx SID_Ghostbytes+$e               // Repeat channel 3
                            lda SID_Ghostbytes+$f
                            jsr GetNote
                            lda SID_Ghostbytes+$14
                            and #$f0
                            lsr
                            lsr           
                            lsr           
                            lsr
                            clc
                            adc #6
                            sta MeterTemp,x
                            jsr CalculateSurroundings
                            rts

GetNote:                    stx NoteLo                  // Input is the node frequency
                            sta NoteHi                  // We'll search the shortened frequency tables to approximate the node playing on a linear scale
                            ldx #8
                            ldy #0                      // Search iteration counter
IterateBinarySearch:        lda FreqTableLookupHilsb,x  
                            sta CompareHi+1
                            lda FreqTableLookupHimsb,x
                            sta CompareHi+2
                            lda FreqTableLookupLolsb,x
                            sta CompareLo+1
                            lda FreqTableLookupLomsb,x
                            sta CompareLo+2

                            lda NoteHi                  // compare high bytes
CompareHi:                  cmp FreqTablePalHi
                            bcc Lower1                  // if NUM1H < NUM2H then NUM1 < NUM2
                            bne Higher1                 // if NUM1H <> NUM2H then NUM1 > NUM2 (so NUM1 >= NUM2)
                            lda NoteLo                  // compare low bytes
CompareLo:                  cmp FreqTablePalLo
                            bcs Higher1                 // if NUM1L >= NUM2L then NUM1 >= NUM2
                            rts

Lower1:                     txa
                            sec
                            sbc Delta,y
                            tax                         
                            iny 
                            cpy #4                      // Control max number of iterations
                            beq Done
                            jmp IterateBinarySearch     

Higher1:                    txa
                            clc
                            adc Delta,y
                            tax
                            iny
                            cpy #4
                            beq Done
                            jmp IterateBinarySearch     

Done:                       rts                         // X register contains the node played approximated to a table of 16 positions.

CalculateSurroundings:      lda MeterTemp-2,x           // Rudimentary, but it works :)
                            cmp MeterTemp,x             // Current band index in X.
                            bcc LeftIsLower             // Take Current band from index-2 and calculate the value between these two and use it for band index-1.
                            lda MeterTemp-2,x           // Do the same for the other side, index1=index2+((index-index2)/2)
                            sec
                            sbc MeterTemp,x
                            lsr
                            clc
                            adc MeterTemp,x
                            sta MeterTemp-1,x
                            jmp LeftSurround
LeftIsLower:                lda MeterTemp,x
                            sec
                            sbc MeterTemp-2,x
                            lsr
                            clc
                            adc MeterTemp-2,x
LeftSurround:               cmp #21
                            bcc !+
                            lda #21
!:                          sta MeterTemp-1,x

                            lda MeterTemp+2,x
                            cmp MeterTemp,x
                            bcc LeftIsLower2
                            lda MeterTemp+2,x
                            sec
                            sbc MeterTemp,x
                            lsr
                            clc
                            adc MeterTemp,x
                            sta MeterTemp+1,x
                            jmp LeftSurround2
LeftIsLower2:               lda MeterTemp,x
                            sec
                            sbc MeterTemp+2,x
                            lsr
                            clc
                            adc MeterTemp+2,x
LeftSurround2:              cmp #21
                            bcc !+
                            lda #21
!:                          sta MeterTemp+1,x
                            rts


FreqTableLookupHilsb:       .for(var i=0; i<16; i++) {          // SID Frequency lsb/msb lookup tables
                                   .byte <FreqTablePalHi+(i*6)
                            }
FreqTableLookupHimsb:       .for(var i=0; i<16; i++) {
                                   .byte >FreqTablePalHi+(i*6)
                            }
FreqTableLookupLolsb:       .for(var i=0; i<16; i++) {
                                   .byte <FreqTablePalLo+(i*6)
                            }
FreqTableLookupLomsb:       .for(var i=0; i<16; i++) {
                                   .byte >FreqTablePalLo+(i*6)
                            }

temp:                       .byte 0
NoteLo:                     .byte 0
NoteHi:                     .byte 0
Delta:                      .byte 4,2,1,0

FreqTablePalLo:
                            .byte $17,$27,$39,$4b,$5f,$74,$8a,$a1,$ba,$d4,$f0,$0e  // Shortened frequency table, because I don't need full granularity.
                            .byte $2d,$4e,$71,$96,$be,$e8,$14,$43,$74,$a9,$e1,$1c  // 2
                            .byte $5a,$9c,$e2,$2d,$7c,$cf,$28,$85,$e8,$52,$c1,$37  // 3
                            .byte $b4,$39,$c5,$5a,$f7,$9e,$4f,$0a,$d1,$a3,$82,$6e  // 4
                            .byte $68,$71,$8a,$b3,$ee,$3c,$9e,$15,$a2,$46,$04,$dc  // 5
                            .byte $d0,$e2,$14,$67,$dd,$79,$3c,$29,$44,$8d,$08,$b8  // 6
                            .byte $a1,$c5,$28,$cd,$ba,$f1,$78,$53,$87,$1a,$10,$71  // 7
                            .byte $42,$89,$4f,$9b,$74,$e2,$f0,$a6,$0e,$33,$20,$ff  // 8
FreqTablePalHi:
                            .byte $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$02  // 1
                            .byte $02,$02,$02,$02,$02,$02,$03,$03,$03,$03,$03,$04  // 2
                            .byte $04,$04,$04,$05,$05,$05,$06,$06,$06,$07,$07,$08  // 3
                            .byte $08,$09,$09,$0a,$0a,$0b,$0c,$0d,$0d,$0e,$0f,$10  // 4
                            .byte $11,$12,$13,$14,$15,$17,$18,$1a,$1b,$1d,$1f,$20  // 5
                            .byte $22,$24,$27,$29,$2b,$2e,$31,$34,$37,$3a,$3e,$41  // 6
                            .byte $45,$49,$4e,$52,$57,$5c,$62,$68,$6e,$75,$7c,$83  // 7
                            .byte $8b,$93,$9c,$a5,$af,$b9,$c4,$d0,$dd,$ea,$f8,$ff  // 8

                            .byte 0,0
dBMeterValue:               .fill 16,0
                            .byte 0,0
MeterTemp:                  .fill 16,0
                            .byte 0,0
               
                            // Quick'n'dirty 180degree + 90degree drop sine.
SoundbarSine:               .byte 0,2,4,5,6,6,5,4,2,0,2,4,6,8,9,10,11,12,13,14,14,15

///////////////////////////////////////////////////////////////////////////////////////////
// Play music using ghostbytes
///////////////////////////////////////////////////////////////////////////////////////////
PlayMusic:                  lda $01                             // Grab SID data. This is called from IRQ.
                            pha
                            lda #$30
                            sta $01
                            jsr $1003
                            ldx #$19
!CopySIDData:               lda $d400,x
                            sta SID_Ghostbytes,x
                            dex
                            bpl !CopySIDData-
                            pla 
                            sta $01
                            ldx #$19
!CopyToSID:                 lda SID_Ghostbytes,x
                            sta $d400,x
                            dex
                            bpl !CopyToSID-
                            rts
base/spectrometer.txt · Last modified: 2017-02-15 17:05 by trap_bonzai