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

%define RADIUS		90			; radius of wheel
%define	BORDER_COLOR	-1			; border color (0 - no border, -1 - changed color, -2 - multicolor)

%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	1			; 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	2			; 1 - allow exit, 2 - more reliable exit, 0 - infinite loop
%define	RESET_COUNTER	1			; 1 - reset sound counter, 0 - save a byte of code :)

%define VIDEO_SHIFT	0			; shift of video page start (0xA000-0x9FFF)*16

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

; Set color to al (depending on values of ah and cl)
%macro	SET_COLOR	0
  %if	INVERSED
		cmp	ah,cl
  %else ; !INVERSED
		cmp	cl,ah
  %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
%endmacro

use16
org	0x100

		; Assume: ax (ah) = 0, bx = 0, word [bx+2] = 0x9FFF, word [sp] = 0

		; Set video mode
		mov	al,0x13
		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	VIDEO_SHIFT
		lds	bp,[bx]			; ds=0x9FFF (as a rule)
%else ; !VIDEO_SHIFT
		push	0xA000
		pop	ds
%endif

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

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

		; Draw wheel
		mov	cl,RADIUS
%if	BORDER_COLOR = -1
		mov	al,dh
%elif	BORDER_COLOR = -2
		mov	al,dl
%elif	BORDER_COLOR
		mov	al,BORDER_COLOR
%endif ; BORDER_COLOR
	.next:
%if	!BORDER_COLOR
		SET_COLOR
%endif ; !BORDER_COLOR
		fld	st0
		fsincos				; st0=cos(angle), st1=sin(angle), st2=angle, st3=delta
	.rep:	mov	[di],cx
		fimul	word [di]		; cx*cos then cs*sin
		fistp	word [di]
		mov	si,[di]
		xchg	bx,si
		cmc
		jc	.rep			; second pass (cos then sin)
		; st0=angle, st1=delta
%if	DIRECTION = 0
		imul	bx,320
%else ; DIRECTION = 1
		imul	si,320
%endif ; DIRECTION
		mov	byte [si+bx+(160+100*320)+VIDEO_SHIFT],al
%if	BORDER_COLOR
		SET_COLOR
%endif ; BORDER_COLOR
		loop	.next
		fadd	st0,st1			; increase angle a tiny bit

		; Play sample
		sub	ah,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	ax			; sometimes it may not work
  %else ; ALLOW_EXIT = 2
		dec	al
  %endif
		jnz	mainloop		; loop if Esc key is not pressed
		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
