;[]=======================================================================[]
;
; flames.z80 -- mini demo for Sprinter
;
; Created by Anton Enin 21-April-1999
;
; Last update 18-July-2002
;
; Copyright (C) 2002 R-lab
;
; This program should be compiled using z80asm
; http://www.zxasm.narod.ru/
;
; z80asm.exe -f bin flames.z80 -o flames.exe
;
;[]=======================================================================[]
; sprinter constants -- see sprinter docs

VPAGE		equ	0x50			; video memory page
YPORT		equ	0x89			; y pos for write to the vram
PAGE0		equ	0x82			; memory page ports
PAGE1		equ	0xA2			;
PAGE2		equ	0xC2                    ;
PAGE3		equ	0xE2                    ;

; os constants -- see os docs

		struc HeaderStruct		; Estex Exe Header
id:		resb	3			; exe file Id (must be 'EXE')
version:	resb	1			; exe file version
code_offeset:	resd	1			; offset to start code (header size)
loader_size:	resw	1			; first loader size
reserved1:	resw	3			; reserved
start_address:	resw	1			; program org
entry_point:	resw	1			; program entry point
stack_point:	resw	1			; program stack point
		endstruc

; my constants -- see ...? :)

PALETTE_BASE	equ	0x8000
FLAMES_FRAME1	equ	0x8000
FLAMES_FRAME2	equ	0x8800

;[]=======================================================================[]

		section .text
		org	0x8FEA

;[]=======================================================================[]
; EXE header

ExeHeader:	istruc HeaderStruct		; initialized structure
		at id,            db 'EXE'
                at version,       db 0
                at code_offeset,  dd CodeStart - ExeHeader
                at loader_size,   dw 0
		at start_address, dw 0x9000
		at entry_point,   dw EntryPoint
		at stack_point,   dw 0xBFFF
		iend
CodeStart:					; start address is 0x9000

;[]=======================================================================[]

EntryPoint:					; org = 0x9000
		ld	hl, HelpString
		ld	c, 0x5C			; print text
		rst	0x10

		call	SetVideoMode
		call	SetPalette
		call	CrearVideoRam

		ld	hl, FLAMES_FRAME1	; clear 2 frame buffer
		ld	de, FLAMES_FRAME1+1
		ld	bc, 0x0FFF
		ld	(hl), l
		ldir

		in	a, (YPORT)		; save inflective ports
		ld	c, a
		in	a, (PAGE1)
		ld	b, a
		push	bc
		ld	a, VPAGE		; set video page to 0x4000-0x7FFF
		out	(PAGE1), a
.main_loop:
						; now scroll frame up to 1 line
		ld	hl, FLAMES_FRAME1+0x40
		ld	de, FLAMES_FRAME1+0x20
		ld	bc, 62*0x20
		call	AccelCopy
						; now add 4 new random pixel
						; to the bottom of the flame
						; hl - last line of frame + 3
		ex	de, hl
		ld	c, 0x06
		add	hl, bc
		ld	b, 0x04
.next_pixel:
		push	hl
		ld	a, 24           	; random 0 - 23
		push	hl             		; for pixel x position
		call	GetRandom
		pop	hl
		add	a, l
		ld	l, a
		ld	a, 180			; pixel intensity 0 - 189
		push	hl
		call	GetRandom
		pop	hl
		ld	(hl), a			; pixel size 2x1
		inc	l
		ld	(hl), a
		pop	hl
		djnz	.next_pixel
                                                ; now blur the frame
						; this is not optimized code ;)
						;   better blur for each pixel
						;   xxx
						;   xox
						;   x x
		ld	ix, FLAMES_FRAME1+0x20
		ld	de, FLAMES_FRAME2+0x20
		ld	h, b
		ld	yh, 0x3F
.blur_line:
		ld	yl, 0x1F
		inc	ix
		inc	de
.blur_pixel:
		ld	l, (ix-0x21)
		ld	c, (ix-0x20)
		add	hl, bc
		ld	c, (ix-0x1F)
		add	hl, bc
		ld	c, (ix-0x01)
		add	hl, bc
		ld	c, (ix+0x01)
		add	hl, bc
		ld	c, (ix+0x1F)
		add	hl, bc
		ld	c, (ix+0x21)
		add	hl, bc
		ld	c, (ix+0x21)		; this equal two pixel deflect
		add	hl, bc			; the flame to left
		ld	a, l
		srl	h
		rra
		srl	h
		rra
		srl	h
		rra
		ld	(de), a
		inc	de
		inc	ix
		dec	yl
		jr	nz, .blur_pixel
		dec	yh
		jr	nz, .blur_line

		halt				; v'sync, maybe :)

						; now copy frame to
						; the vram
						; 60 lines from top
		ld	hl, FLAMES_FRAME2
		ld	de, 0x4080
		ld	b, 62
		di
		ld	d, d			; set accel count
		ld	a, 0x20
		ld	b, b

		ld	a, b
.out_loop:
		out	(YPORT), a
		ld	l, l			; accel copy
		ld	a, (hl)
		ld	(de), a
		ld	b, b
		ld	a, l
		add	a, 0x20
		ld	l, a
		adc	a, h
		sub	l
		ld	h, a			; next line in frame buffer
		inc	b
		ld	a, b			; next y line in vram
		cp	62+60
		jr	nz, .out_loop
		ei

		ld	hl, FLAMES_FRAME2	; duplicate frame buffers
		ld	de, FLAMES_FRAME1
		ld	bc, 0x0800
		call	AccelCopy

		ld	c, 0x31			; test keypress
		rst	0x10
		jp	z, .main_loop
		sub	0x1B			; ESC is pressed?
		jp	nz, .main_loop

		pop	bc
		ld	a, b
		out	(PAGE1), a
		ld	a, c
		out	(YPORT), a		; restore ports

		call	CrearVideoRam
		call	ResVideoMode

		ld	bc, 0x0041
		rst	0x10      		; exit to OS...


;[]=======================================================================[]
; Simple random numbers generator
; Input:
;	a - random number limit
; Output:
;	a - random number
GetRandom:
		ld	c, a
.seed:		ld	de, 0x4664
		ld	h, e
		ld	l, 0xFD
		ld	a, d
		and	a
		sbc	hl, de
		and	a
		sbc	hl, de
		ld	e, a
		ld	d, 0x00
		and	a
		sbc	hl, de
		jr	nc, .next
		inc	hl
.next:		ld	(.seed + 1), hl
						; now c - limit
						;     l - random 0.0 - 1.0
		ld	e, l
		ld	d, 0x00
		ld	l, d
		ld	h, c			; random * limit = number 8.8
		%rep	8
		add	hl, hl
		jr	nc, $+3
		add	hl, de
		%endrep
		ld	a, h			; return rnd (0 < x < limit)
		ret

;[]=======================================================================[]
; set requist video mode 640x256x16
SetVideoMode:
		ld	c, 0x51			; save previos vmode
		rst	0x10
		ld	(vmode + 1), a
		ld	a, b
		ld	(vscrn + 1), a
		sub	a
		call	CrearVideoRam
		ld	bc, 0x0050		; set 320x256x256
		ld	a, 0x81
		rst	0x10
		ret

;[]=======================================================================[]
; restore previos video mode
ResVideoMode:
		sub	a
		call	CrearVideoRam
vscrn:		ld	b, 0x00
vmode:		ld	a, 0x00
		ld	c, 0x50
		rst	0x10			; set previos vmode
		ret

;[]=======================================================================[]
SetPalette:
		ld	hl, PALETTE_BASE	; generate palette
		ld	bc, 0x0800
.loop1:		ld	a, 0x10			; gradien of red color
		sub	b
		add	a, a
		add	a, a
		add	a, a
		add	a, a
		add	a, a
		ld	(hl), c
		inc	hl
		ld	(hl), c
		inc	hl
		ld	(hl), a
		inc	hl
		ld	(hl), c
		inc	hl
		djnz	.loop1
		ld	b, 0x10

.loop2:		ld	(hl), c         	; gradien of yellow color
		inc	hl
		ld	a, 0x10
		sub	b
		add	a, a
		add	a, a
		add	a, a
		add	a, a
		ld	(hl), a
		inc	hl
		ld	(hl), 240
		inc	hl
		ld	(hl), c
		inc	hl
		djnz	.loop2

		ld	b, 0x10
.loop3:		ld	a, 0x10			; gradien of white color
		sub	b
		add	a, a
		add	a, a
		add	a, a
		add	a, a
		ld	(hl), a
		inc	hl
		ld	(hl), 240
		inc	hl
		ld	(hl), 240
		inc	hl
		ld	(hl), c
		inc	hl
		djnz	.loop3

		ld	b, 208
		ld	a, 252
InPal4		ld	(hl), a         	; gradien of :) super white color
		inc	hl
		ld	(hl), a
		inc	hl
		ld	(hl), a
		inc	hl
		ld	(hl), c
		inc	hl
		djnz	InPal4

		ld	hl, PALETTE_BASE
		ld	de, 0x0000
		ld	bc, 0xFFA4
		sub	a
		rst	0x08			; load palette by BIOS

		ret

;[]=======================================================================[]
; Clear video memory (first screen)
CrearVideoRam:
		in	a, (YPORT)		; store modify ports
		ld	c, a
		in	a, (PAGE1)
		ld	b, a
		push	bc
		ld	a, VPAGE
		out	(PAGE1), a
		ld	hl, 0x4000
		ld	bc, 320			; screen x size in bytes
		ld	e, l
		di
		ld	d, d
		ld	a, 0x00			; set accel lenght to 256 bytes
.loop:		ld	e, e			; fill vertical lines
		ld	(hl), e
		ld	b, b
		inc	hl
		dec	bc
		ld	a, b
		or	c
		jr	nz, .loop
		ei
		pop	bc
		ld	a, b
		out	(PAGE1), a
		ld	a, c
		out	(YPORT), a		; restore modify ports
		ret

;[]=======================================================================[]
; Copy memory with sprinter accel
; Input:
;	hl - from
;	de - to
;	bc - size
; Output:
;	hl - hl + size
;	de - de + size

AccelCopy:
		ld	a, b
		or	a
		jr	z, .next
		di
		ld	d, d			; set accel count to 256
		ld	a, 0x00
.loop1:		ld	l, l
		ld	a, (hl)
		ld	(de), a
		ld	b, b
		inc	h
		inc	d
		djnz	.loop1
		ei
.next:
		ld	a, c
		or	a
		ret	z
		ld	(.count + 1), a
		di
		ld	d, d
.count:		ld	a, 0x00
		ld	l, l
		ld	a, (hl)
		ld	(de), a
		ld	b, b
		ei
		ld	a, c
		add	a, l
		ld	l, a
		ld	a, h
		adc	a, l
		sub	l
		ld	h, a			; update hl
		ld	a, c
		add	a, e
		ld	e, a
		ret	nc
		inc	d			; update de
		ret

;[]=======================================================================[]

HelpString:
		db	0x0D, 0x0A
		db	"The Flames! Mini demo for Sprinter.", 0x0D, 0x0A
		db	"Created by Enin Anton.", 0x0D, 0x0A, 0x00

