; [ Hypno Wheel ] 64-byte intro
; (c) 2019 by Jin X (Telegram: @jinxonik / jin_x@list.ru)

%define	VIDEO_MODE	0x103			; video mode (VESA mode if >= 0x100)
%define	WHEEL_POS	0			; 0 - draw wheel in center of screen, 1 - draw wheel on SCREEN_Y,SCREEN_Y coordinates, 2 - draw wheel on 256,256 coordinates, 3 - draw wheel on 512,256 coordinates
%define CENTER_X	800 / 2			; coordinates of...
%define CENTER_Y	600 / 2			; ...screen center

%define RADIUS		256			; radius of wheel (value 256 adds a frame)
%define ANIMATION	1			; type of animation (0..4, 5..9)
%define INVERSED	0			; 0 - normal mode, 1 - inversed mode
%define	DIRECTION	0			; direction of rotation (0 or 1)
%define	SPECEFFECT	3			; 1,2, 3..5 - add a special effect (don't use SPECEFFECT >= 3 with INVERSED = 1 mode!); mode SPECEFFECT >= 3 adds noises to sound, 0 - don't

%define	TUNE_TIMER	1			; 1 - tune timer, 0 - don't
%define	ALLOW_EXIT	1			; 1 - allow exit by Esc key, 2 - allow exit by any key, 0 - infinite loop
%define	RESET_COUNTER	1			; 1 - reset sound counter, 0 - save a byte of code :)

;-----------------------------------------------------------------------------------------------------------------------

use16
org	0x100

		; Set video mode
%if	VIDEO_MODE >= 0x100
		mov	ax,0x4F02
		mov	bx,VIDEO_MODE
%else ; VIDEO_MODE < 0x100
		mov	al,VIDEO_MODE
%endif ; VIDEO_MODE
		int	0x10
%if	ANIMATION == 0
		fldlg2				; 0.301029995664 (delta angle)
%elif	ANIMATION == 1
		fldln2				; 0.6931471805599 (delta angle)
%elif	ANIMATION == 2
		fld1				; 1 (delta angle)
%elif	ANIMATION == 3
		fldl2e				; 1.442695040889 (delta angle)
%elif	ANIMATION == 4
		fldl2t				; 3.321928094887 (delta angle)
%elif	ANIMATION > 4
		fld	dword [byte si+delta-0x100] ; delta angle
%endif ; ANIMATION
		fldz				; angle

%if	TUNE_TIMER
		; Tune timer (PIT 8253/8254)
		mov	al,0x24			; set higher byte only
		out	0x43,al
		mov	al,2			; PIT counter divisor (1193182 / 0x200 = 2330 Hz)
		out	0x40,al
%endif ; TUNE_TIMER

%if	RESET_COUNTER
		cwd				; dx = 0 (counter)
%endif ; RESET_COUNTER

mainloop:
		; Calculate sample
		; t & (t>>8)
		mov	al,dl			; ah = t
		and	al,dh			; ah = t & t>>8
		inc	dx			; increase counter

		; Draw wheel
%if	VIDEO_MODE >= 0x100
  %if	RADIUS % 256 = 0
		mov	bh,RADIUS / 256
  %elif	RADIUS < 256
		mov	bl,RADIUS
  %else
		mov	bx,RADIUS
  %endif ; RADIUS % 256
%else ; VIDEO_MODE < 0x100
		mov	bl,RADIUS
%endif ; VIDEO_MODE
	.next:
		pusha
%if	INVERSED
		cmp	al,bl
%else ; !INVERSED
		cmp	bl,al
%endif ; INVERSED
		salc				; al = 0 or 0xFF
%if	SPECEFFECT = 1
		daa
%elif	SPECEFFECT = 2
		das
%elif	SPECEFFECT = 3
		aaa
%elif	SPECEFFECT = 4
		daa
		aaa
%elif	SPECEFFECT = 5
		das
		aaa
%endif ; SPECEFFECT
		and	al,dh

		fld	st0
		fsincos				; st0=cos(angle), st1=sin(angle), st2=angle, st3=delta
	.rep:	mov	[si],bx
		fimul	word [si]		; bx*cos then bx*sin
		fistp	word [si]
%if	DIRECTION = 0
		mov	cx,[si]
  %if	WHEEL_POS = 1
		add	cx,CENTER_Y
  %elif	WHEEL_POS >= 2
		inc	ch
  %endif ; WHEEL_POS
%else ; DIRECTION = 1
		mov	dx,[si]
  %if	WHEEL_POS = 1
		add	dx,CENTER_Y
  %elif	WHEEL_POS >= 2
		inc	dh
  %endif ; WHEEL_POS
%endif ; DIRECTION
		xchg	dx,cx
		dec	di
		jpo	.rep			; second pass (cos then sin)
		; st0=angle, st1=delta
%if	WHEEL_POS = 0
		add	cx,CENTER_X
		add	dx,CENTER_Y
%elif	WHEEL_POS = 3
		inc	ch
%endif ; WHEEL_POS
		mov	ah,0x0C
		int	0x10			; put pixel
		popa
		dec	bx
		jnz	.next
		fadd	st0,st1			; increase angle a tiny bit

		; Play sample
		sub	al,1
		salc
;		and	al,2			; can be omitted for economy :)
		out	0x61,al			; set speaker position

%if	TUNE_TIMER
		hlt				; delay
%endif ; TUNE_TIMER

%if	!ALLOW_EXIT
		jmp	mainloop		; infinite loop
%else ; ALLOW_EXIT
		in	al,0x60
  %if	ALLOW_EXIT = 1
		dec	al
		jnz	mainloop		; loop if Esc key is not pressed
  %else ; ALLOW_EXIT = 2
		das
		jp	mainloop		; loop if not key is pressed
  %endif
		ret
%endif ; !ALLOW_EXIT

%if	ANIMATION = 5
		db	0x3B
%elif	ANIMATION = 6
		db	0x3C
%elif	ANIMATION = 7
		db	0x3D
%elif	ANIMATION = 8
		db	0x3E
%elif	ANIMATION = 9
		db	0x3F
%endif ; ANIMATION
delta		equ	$-4
