
//#resource "vcs-ca65.h"

import "vcslib.ecs"

component Song
  songdata: array of 0..255
end

// TODO: merge with SoundChannel
component MusicChannel
  duration: 0..255
  note: 0..255
  duty: 0..255
end

component MusicPlayer
  timer: 0..255 default 255
  channel: [MusicChannel]
  songptr: 0..65535
  volume: 0..15 default 15
  tempo: 0..255 default 7
end


// Table of AUDF base values for each note
resource FREQZ ---
  .byte 30, 30, 30, 30, 30, 28, 26, 25, 23, 22, 21, 19, 18, 17, 16, 15, 14, 13, 12, 12, 11, 10, 10, 9, 8, 8, 7, 7, 6, 6, 5, 5, 30, 29, 27, 25, 24, 22, 21, 20, 19, 18, 16, 15, 15, 14, 13, 12, 11, 11, 10, 31, 29, 27, 25, 24, 23, 21, 20, 19, 18, 16, 15, 15
---
// Table of duty-cycle bits for each note
resource DUTYZ ---
  .byte 247, 247, 247, 247, 1, 73, 219, 1, 219, 73, 0, 219, 181, 85, 85, 85, 181, 219, 247, 1, 73, 181, 0, 73, 219, 17, 219, 17, 219, 73, 247, 85, 247, 1, 85, 247, 73, 247, 181, 17, 1, 0, 247, 247, 0, 1, 17, 73, 181, 0, 17, 0, 1, 85, 247, 73, 0, 181, 73, 1, 0, 247, 247, 0
---
// Table of AUDC values for each note
resource TONEZ ---
  .byte 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
---

system MusicPlayer
  on preframe do once
  ---
  {{!musicpulse}}	; update song
  {{!musicframe}}	; update registers
  ---
  on postframe do once
  ---
  {{!musicframe}}	; update registers
  ---
  // TODO: unroll?
  on musicframe do foreach [MusicChannel]
  ---
; Update channel pitch in AUDF0
; 8-bit rotation of duty cycle bits
	lda	{{get duration}}
        beq	:++
	lda	{{set duty}}
        asl
        bcc	:+
        ora	#1
:	sta	{{set duty}}
	lda	{{get note}}
        beq	:+
; If next bit is set, add 1 to AUDF0 
        adc	#0
        sta	AUDF0,x
:
  ---
  on musicpulse do foreach [MusicChannel]
  ---
; Decrement the volumes for each channel
; Also decrement next-note timer, fetch next note
	lda	{{get duration}}
        beq	:+
        lsr
        sta	AUDV0,x
        dec	{{set duration}}
:
---
  on musicpulse do with [MusicPlayer]
  ---
        lda	{{get timer}}
        bmi	@Done
        beq	@NextData
        dec	{{set timer}}
        jmp	@Done
; Timer ran out, so fetch next note
@NextData:
	ldx	#0
        lda	({{get songptr}},x)
        bmi	@LoadDuration
; < $80, play next note
        ldx	{{get channel}}	; next channel
        tay
        {{!musicnote}}
        inx
        txa
        and	#1
        sta	{{set channel}}	; inc next channel
        jmp	@IncDataPtr
; >= $80, load next duration
@LoadDuration:
	cmp	#$ff		; $ff = end of song
        bne	@NoResetTrack
        sta     {{set timer}}
        {{!musicdone}}
        jmp     @Done
@NoResetTrack:
        and	#$7f
;        asl
        sta	{{set timer}}	; store duration * 2
@IncDataPtr:
; increment song pointer
	inc	{{set songptr 0}}
        bne	@Done
	inc	{{set songptr 8}}
@Done:
  ---
  // TODO: should use "with"?
  on musicnote do select all [MusicChannel]
  ---
; Play a note
; X = channel (0,1)
; Y = note index (0-63)
	lda	{{^FREQZ}},y
        sta	{{base note}},x
	lda	{{^DUTYZ}},y
        sta	{{base duty}},x
        lda	{{^TONEZ}},y
        sta	AUDC0,x
; TODO: consts?
        lda	{{get MusicPlayer:tempo}}
        sta	{{base duration}},x
        lda	{{get MusicPlayer:volume}}
        sta	AUDV0,x
  ---
  on playmusic do foreach [MusicPlayer] limit 1
  ---
  lda	#<{{arg 0}}
  sta	{{set songptr 0}}
  lda	#>{{arg 0}}
  sta	{{set songptr 8}}
  lda   #0
  sta   {{set timer}}
  ---
  on stopmusic do foreach [MusicPlayer] limit 1
  ---
  lda   #$ff
  sta   {{set timer}}
  ---
end

///

demo music
  using FrameLoop
  using MusicPlayer
  entity [MusicChannel] end
  entity [MusicChannel] end
  entity MusicPlayer [MusicPlayer]
    const volume = 10
    const tempo = 31
  end
  system music
    on musicdone do with [MusicPlayer]
    ---
    ; TODO: nested exprs
    ; {{!playmusic ^SampleMusic}}
    ;{{^SampleMusic}}
    {{!playmusic SampleMusic}}
    ---
    on preframeloop do once
    ---
    {{!musicdone}}
    ---
  end
end demo

resource SampleMusic ---
 .byte $35,$41,$8a,$37,$43,$8a,$33,$3f,$8a,$30,$3c,$94,$3e,$32,$8a,$3a,$2e,$94,$35,$29,$8a,$37,$2b,$8a,$33,$27,$8a,$30,$24,$94,$32,$26,$8a,$2e,$22,$94,$29,$1d,$8a,$2b,$1f,$8a,$27,$1b,$8a,$24,$18,$94,$1a,$26,$8a,$18,$24,$8a,$17,$23,$8a,$16,$22,$a8,$3a,$35,$ff
---

