; Title: "NewKidOnTheBlock" ; Chip: 6581 C64 used when composing. ; Author: Frantic/Hack'n'Trade ; https://codebase64.org/doku.php?id=base:256_bytes_tune_player ;--------------------------------- ; CONSTANTS ; DEBUG = 1 NUMBEROFVOICES = 3 ;Using all three Voices S = $10 ;Song "speed" ;--------------------------------- ; ZERO PAGE ADDRESSES ; TICKCOUNTERS = $02 ;Use zp for tickcounters.. TICKCOUNTERS+0, TICKCOUNTERS+7, ;TICKCOUNTERS+14 will be used. DATAPOS = $03 ;Use zp for SONGPOSITIONS... 0, 7, 14.. FILTERHI = $23 ;--------------------------------- ; CODE ; include "cartheader.dasm" Start: sei ldy #$02 ;Set it to $02 so the first incorrectly timed ;iteration won't affect anything sty FILTERHI ;Init filtersweep to make it a bit more deterministic. ;Init counters and sonpositions lda #(NUMBEROFVOICES-1)*7 ;Voice indeX _initlp: tax sta DATAPOS,x ;Init datapos+0 to 0, datapos+7 to 7 and datapos+14 to 14. sty TICKCOUNTERS,x ;y can be anything.. doesn't matter, but now I happen to ;init the filter so set it to $01 too sec sbc #7 bpl _initlp ;-- ; Main player loop _outerloop: _wrast: cpx $d012 ;After the loop, x is a negative number, thus well above $3e ;(or whatever the critical raster value is again..) bne _wrast ;First iteration won't be correct though, but since the ;counters are set to 2 initially it doesn't matter. #if DEBUG lda #1 sta $d020 sta $d021 #endif jsr sweep stx $d416 ;filter hi ldx #(NUMBEROFVOICES-1)*7 ;Voice indeX ;Right here is a good place for Voice specific code, if there is any reason for that. _innerloop: dec TICKCOUNTERS,x bne _loopend ;Time for new sound settings, turn gate and oscillator off.. lda #8 sta $d404,x ;Turn on global volume to make sure we'll hear anything at all. ;Reason for having this code *inside* the loop: ; To make sure the player won't run too fast and get executed ; twice on the same rasterline. lda #$5f ;Hi-pass + Lo-pass filter on. sta $d418 lda #$a2 ;Filter used on middle (melody) voice. sta $d417 ;Parse data sequence data ldy DATAPOS,x _newseq: iny lda _musicdata-1,y ;Get databyte bne _nonewseq lda _musicdata-0,y ;Get jumpval if it's jumptime tay ;..and use new one instead bpl _newseq ;This means data may not be larger than $80 bytes _nonewseq: sty DATAPOS,x pha and #$0f ;Note value tay lda _freqhi,y sta $d401,x ;Freq hi lda _freqlo,y sta $d400,x ;Freq lo pla lsr lsr lsr lsr tay lda adtab,y sta $d405,x lda durtab,y sta TICKCOUNTERS,x lda ctrltab,y sta $d404,x _loopend: txa .byte $cb,7 ;axs #7 / sbx #7 / whatever.. bpl _innerloop #if DEBUG dec $d020 dec $d021 jmp _outerloop #endif bmi _outerloop sweep inc $23 ldx $23 ldy #$00 rts ;--------------------------------- ; "Instruments" ; ; Using AD only to save space. (SR is set to 00 as default) ; ; A pattern is S*8 ticks long, so using "instrument" 5 we ; can represent a whole empty pattern by just one byte in ; the sequence data. At the same time, this format allows ; for changes to the waveform every tick, which means we ; can also make drums and such things. But, not in this ; tune. Perhaps in the next one.. ; ; 00 01 02 03 04 05 06 adtab: .byte $1c, $1b, $cd, $2b, $1a, $00, $ad ctrltab: .byte $11, $11, $21, $21, $21, $00, $21 durtab: .byte S*1, S*3, S*6, S*2, S*1, S*8, S*8 ;--------------------------------- ; Sequence data ; ; Voc0 starts at 0 ; Voc1 starts at 7 ; Voc2 starts at 14 ; ; Note and duration stored in one byte and the byte following a $00 (JP) ; byte is interpreted as the destination of a jump to another place in ; the data. _musicdata: _Voc0start: .byte $00 | G4 .byte $00 | Az4 .byte $00 | C5 .byte $00 | D5 .byte $00 | Dz5 .byte JP .byte <(_Voc0komp-_musicdata) _Voc1start: .byte $20 | D5 .byte $30 | Dz5 .byte $20 | D5 .byte $40 | A4 .byte $40 | Az4 .byte JP .byte <(_Voc1melody-_musicdata) _Voc2start: ;- .byte $50 | 0 .byte $50 | 0 .byte $50 | 0 .byte $50 | 0 ;- .byte $50 | 0 _Voc2loop: .byte $50 | 0 .byte $50 | 0 .byte $20 | C5 .byte $40 | Dz4 .byte $40 | F4 ;- .byte $50 | 0 .byte $60 | D5 .byte $20 | Dz5 .byte $30 | Az4 .byte $20 | A4 .byte $30 | F4 ;- .byte $60 | G4 .byte $50 | 0 .byte $50 | 0 .byte $60 | C5 ;- .byte $50 | 0 .byte $60 | D5 .byte $60 | Dz5 .byte $60 | F5 ;- .byte $60 | G5 .byte JP .byte <(_Voc2loop-_musicdata) _Voc0komp: .byte $10 | G5 .byte $00 | D4 .byte $00 | Fz4 .byte $00 | A4 .byte $00 | Az4 .byte $00 | C5 .byte $10 | D5 .byte $00 | Dz4 .byte $00 | F4 .byte $00 | G4 .byte $00 | Az4 .byte $00 | C5 .byte $10 | Dz5 .byte $00 | F4 .byte $00 | A4 .byte $00 | C5 .byte $00 | D5 .byte $00 | Dz5 .byte $10 | F5 .byte JP .byte <(_Voc0start-_musicdata) _Voc1melody: .byte $20 | C5 .byte $30 | Az4 .byte $20 | A4 .byte $30 | F4 ;- .byte $60 | G4 .byte $50 | 0 .byte $20 | F4 .byte $30 | Dz4 .byte $20 | F4 .byte $30 | Dz4 ;- .byte $20 | D4 .byte $30 | Dz4 .byte $20 | D4 .byte $30 | Fz4 .byte $20 | G4 .byte $30 | Dz4 .byte $00 | C4 .byte $00 | Dz4 .byte $00 | F4 .byte $00 | G4 .byte $00 | A4 .byte $00 | C5 .byte $00 | G5 .byte $00 | F5 .byte JP .byte <(_Voc1start-_musicdata) ;--------------------------------- ;Note freq data ; ; Only using needed notes. JP = 0 C4 = 1 D4 = 2 Dz4 = 3 ;E4 = 2;Not used F4 = 4 Fz4 = 5 G4 = 6 ;Gz4 = 6;Not used A4 = 7 Az4 = 8 ;B4 = 8;Not used C5 = 9 ;Cz5 = 9;Not used D5 = 10 Dz5 = 11 ;E5 = 11;Not used F5 = 12 ;Fz5 = 13;Not used G5 = 13 ;Gz5 ;Not used ;A5 ;Not used ;Az5 = 14;Not used _freqlo = *-1 ;-1 to exclude the corresponding first byte (in _freqhi) used as a "wrap flag" .byte $77 .byte $61,$e1 .byte $f7,$8f,$30 .byte $8f,$4e .byte $ef .byte $c3,$c3 .byte $ef .byte $60 _freqhi: .byte $00 ;WRAP .byte $07 .byte $08,$08 .byte $09,$0a,$0b .byte $0c,$0d .byte $0e .byte $10,$11 .byte $13 .byte $16