; Lo-res fire animation, size-optimized

; by deater (Vince Weaver) <vince@deater.net>

; based on code described here http://fabiensanglard.net/doom_fire_psx/

FIRE_YSIZE=20

	;=======================
	; fire_init
	;=======================
	; call at beginning to clear out framebuffer

fire_init:
	lda	#<fire_framebuffer
	sta	FIRE_FB_L

	lda	#>fire_framebuffer
	sta	FIRE_FB_H

	ldx	#FIRE_YSIZE
clear_fire_loop:

	lda	#0
	ldy	#39
clear_fire_line_loop:
	sta	(FIRE_FB_L),Y
	dey
	bpl	clear_fire_line_loop
done_fire_line_loop:

	clc
	lda	FIRE_FB_L
	adc	#40
	sta	FIRE_FB_L
	bcc	skip_inc_hi
	inc	FIRE_FB_H
skip_inc_hi:

	dex
	bne	clear_fire_loop

	lda	#7
	;fall through
	;jsr	fire_setline

	;rts



	;======================================
	; set bottom line to color in A
	;======================================
	; Set to 7 to init, Set to 0 to have fire go out
fire_setline:

	ldx	#<(fire_framebuffer+(FIRE_YSIZE-1)*40)
	stx	FIRE_FB_L

	ldx	#>(fire_framebuffer+(FIRE_YSIZE-1)*40)
	stx	FIRE_FB_H

	ldy	#39
set_fire_line:
	sta	(FIRE_FB_L),Y
	dey
	bpl	set_fire_line
done_set_fire_line:

	rts


	;===============================
	;===============================
	; Draw fire frame
	;===============================
	;===============================

draw_fire_frame:

	;===============================
	; Update fire frame
	;===============================
	; Unroll x 2

	; set up working line (to store to)

	lda	#<fire_framebuffer					; 2
	sta	fire_smc5_fb+1						; 4
	lda	#>fire_framebuffer					; 2
	sta	fire_smc5_fb+2						; 4

	; set up input line, one below,  (to load from)

	lda	#<(fire_framebuffer+40)					; 2
	sta	fire_smc5_fb2+1						; 4
	lda	#>(fire_framebuffer+40)					; 2
	sta	fire_smc5_fb2+2						; 4


	; loop X (ypos) from 0 - FIRE_YSIZE-1

	ldx	#0							; 2
fire_fb_update:

	; FIXME: optimize
	;	original = ??? (complex)
	; TODO: unroll once and combine with the lores copy on even lines?


	; Loop Y (xpos) from 39 to 0
	ldy	#39							; 2
fire_fb_update_loop:

	; Get random 16-bit number

	jsr	random16						; 40

	; A randomly has either SEEDL or SEEDH here, I guess that's enough?

	; get random number Q 0..3
	; Q used to see if whether we grab same lower value or if decrement

	and	#$3							; 2
	sta	FIRE_Q							; 3

	; 0..12 = A volume, 13-25 = B volume, 26-39 = C volume

	lda	A_VOLUME						; 3
	cpy	#13							; 2
	bcc	fire_vol	; blt					; 2/3
	lda	B_VOLUME						; 3
	cpy	#26							; 2
	bcc	fire_vol	; blt					; 2/3
	lda	C_VOLUME						; 3
fire_vol:
	and	#$f							; 2

	; adjust fire height with volume

	cmp	#$8							; 2
	bcs	fire_medium	; bge					; 2/3
fire_low:
	; Q=1 3/4 of time
	lda	FIRE_Q							; 3
	bne	fire_one	; 0-3, is not 0 3/4 of time		; 2/3
	beq	fire_set						; 3
fire_medium:
	cmp	#$d							; 2
	lda	FIRE_Q							; 3
	bcs	fire_high	; bge					; 2/3

	; Q=1 1/2 of time
	and	#$1							; 2
	bcc	fire_set	; branch always				; 3
fire_high:
	; Q=1 1/4 of time
	;;lda	FIRE_Q		; is 0 1/4 of time			; 3
	bne	fire_zero						; 2/3
fire_one:
	lda	#1							; 2
	bne	fire_set						; 3
fire_zero:
	lda	#0							; 2
fire_set:
	sta	FIRE_Q							; 3



	;=================
	; check if flame from left/right

	; store Y as we over-write it
	sty	FIRE_Y							; 3

	; bounds check
	; on edges, don't wrap

	tya								; 2
	beq	fire_r_same						; 2/3
	cpy	#39							; 2
	beq	fire_r_same						; 2/3

	; RAND_R
	; 50% chance fire comes from below
	; 25% chance comes from right
	; 25% change comes from left

	lda	SEEDH							; 3
	lsr								; 2
	and	#$1							; 2
	bne	fire_r_same						; 2/3
	bcc	r_up							; 2/3
r_down:
	dey								; 2
	.byte	$a9							; 2
r_up:
	iny								; 2
fire_r_same:

	; get next line color
fire_smc5_fb2:
	lda	$1234,Y							; 4+

	; restore saved Y
	ldy	FIRE_Y							; 3

	; have value, subtract Q
	sec								; 2
	sbc	FIRE_Q							; 3

	; saturate to 0
	bpl	fb_positive						; 2/3
	lda	#0							; 2
fb_positive:

	; store out
fire_smc5_fb:
	sta	$1234,Y			; store out			; 5

	dey								; 2
	bpl	fire_fb_update_loop					; 2/3

done_fire_fb_update_loop:

	; if just finished odd line, we are already set up
	; to do the framebuffer copy for the prev two lines?



	; complicated adjustment
	clc								; 2
	lda	fire_smc5_fb+1						; 4
	adc	#40							; 2
	sta	fire_smc5_fb+1						; 4
	bcc	skip_fb_inc1						; 2/3
	inc	fire_smc5_fb+2						; 4
	clc								; 2
skip_fb_inc1:

	lda	fire_smc5_fb2+1						; 4
	adc	#40							; 2
	sta	fire_smc5_fb2+1						; 4
	bcc	skip_fb2_inc1						; 2/3
	inc	fire_smc5_fb2+2						; 4
skip_fb2_inc1:

	inx								; 2
	cpx	#(FIRE_YSIZE-1)						; 2
	;beq	fire_update_done					; 2/3
	bne	fire_fb_update						; 3

fire_update_done:



	;===================================
	; copy framebuffer to low-res screen
	;===================================

	lda	#<fire_framebuffer					; 2
	sta	fire_smc_fb+1						; 5
	lda	#<(fire_framebuffer+40)					; 2
	sta	fire_smc_fb2+1						; 5

	lda	#>fire_framebuffer					; 2
	sta	fire_smc_fb+2						; 5
	;this lda could be omitted if values match
	lda	#>(fire_framebuffer+40)					; 2
	sta	fire_smc_fb2+2						; 5

	lda	#16							; 2
	sta	FIRE_FB_LINE						; 3

	ldx	#(FIRE_YSIZE/2)						; 2

fire_fb_copy:

	ldy	FIRE_FB_LINE						; 3
	iny								; 2
	iny								; 2
	sty	FIRE_FB_LINE						; 3


	; Set up output, self-modifying code
	lda	gr_offsets,Y						; 4+
	sta	fire_smc_outl+1						; 4
	lda	gr_offsets+1,Y						; 4+
	clc								; 2
	adc	DRAW_PAGE						; 3
	sta	fire_smc_outl+2						; 4

	; FIXME: below, can we do this better?
	; 50: original code
	; 39: move to big lookup table
	; 33: move save/restore X outside inner loop
	; 30: self-modifying code

	ldy	#39							; 2
	stx	FIRE_X							; 3
fire_fb_copy_loop:
	; get top byte
	; note, seems backwards, apple II LORES bottom byte is on top
fire_smc_fb2:
	lda	$1234,Y							; 4+
	asl								; 2
	asl								; 2
	asl								; 2
	; get bottom byte
fire_smc_fb:
	ora	$1234,Y							; 4+
	tax								; 2
	lda	fire_colors,X						; 4+
fire_smc_outl:
	sta	$1234,Y		; store out				; 5

	dey								; 2
	bpl	fire_fb_copy_loop					; 2/3
done_fire_fb_copy_loop:
	ldx	FIRE_X							; 3


	; complicated adjustment
	clc								; 2
	lda	fire_smc_fb+1						; 4
	adc	#80							; 2
	sta	fire_smc_fb+1						; 5
	bcc	skip_fb_inc2						; 2/3
	inc	fire_smc_fb+2						; 5
	clc								; 2
skip_fb_inc2:

	lda	fire_smc_fb2+1						; 4
	adc	#80							; 2
	sta	fire_smc_fb2+1						; 5
	bcc	skip_fb2_inc2						; 2/3
	inc	fire_smc_fb2+2						; 5
skip_fb2_inc2:

	dex								; 2
	bne	fire_fb_copy						; 2/3


	rts								; 6

;fire_colors_low:	.byte	$00,$00,$03,$02,$06,$07,$0E,$0F
;fire_colors_high:	.byte	$00,$00,$30,$20,$60,$70,$E0,$F0

fire_colors:
	; 0   1	  2   3	  4   5	  6   7
	; 0   0	  3   2	  6   7	  e   f
.byte	$00,$00,$03,$02,$06,$07,$0e,$0f		; 0
.byte	$00,$00,$03,$02,$06,$07,$0e,$0f		; 0
.byte	$30,$30,$33,$32,$36,$37,$3e,$3f		; 3
.byte	$20,$20,$23,$22,$26,$27,$2e,$2f		; 2
.byte	$60,$60,$63,$62,$66,$67,$6e,$6f		; 6
.byte	$70,$70,$73,$72,$76,$77,$7e,$7f		; 7
.byte	$e0,$e0,$e3,$e2,$e6,$e7,$ee,$ef		; e
.byte	$f0,$f0,$f3,$f2,$f6,$f7,$fe,$ff		; f

	; FIXME: just reserve space in our memory map
fire_framebuffer:
	.res    40*FIRE_YSIZE, $00