; AX = 0000
; BX = 0000
; CX = 00FF
; DX = CS
; SI = 0100
; DI = FFFE
; BP = 09xx
; SP = FFFC / FFFE

; + INIT
org 100h
mov al, 0x13 ; video mode : 320x200 : DI will move from 0 to 64 000
int 0x10	 ; switch video mode to AL

push 0xa000
pop es

xor bp, bp

;;;; PALETTE
; .::. .::. .::. .::. .::. .::. .::. .::.
palette:
mov dx, 0x3c8
xor ax, ax ; start at color 0
out dx, al ; select palette colors
inc dx

color:
	; get color palette index i (0 - 255) into BX
	mov ax, cx
	xor al, 0xFF ; i : 0 - 255
	push ax
	
	imul ax, ax, 9 ; i * 9
	xor al, 0xFF ; - i * 9
	and ax, 0x3F ; (-i * 9) % 0x3F
	push ax
	
	brighten_red: sub ax, 63 ; brighten over time, linearly : 63 -> 0
	
	jns color2
		xor ax, ax ; set to 0 instead of under-flowing, so we fade to black
	color2:
	
	; red1 = 0
	; red2 = (58 - i * 9)
	out dx, al ; RED
		
	pop ax ; brightness : (-i * 9) % 0x3F
	pop bx ; i
		
	darken_green: shr bx, 0 ; darken over time, dividing by a power of 2 : 0 -> 2
	sub ax, bx ; brightness - i
	
	jns color4
		xor ax, ax ; set to 0 instead of under-flowing
	color4:
	
	out dx, al ; GREEN
	
	xor al, 0x09 ; creates a blue-ish background at the start, and yellow-ish accents at the end
	out dx, al ; BLUE
loop color
;;;; END PALETTE
; .::. .::. .::. .::. .::. .::. .::. .::.

; + PIXEL LOOP
pixel: ; will draw the whole screen, pixel per pixel
	push cx ; save loop counter
			
	mov ax, 0xCCCD ; rrrola constant
	mul di ; get screen position (256 x 256) into DH (y) and DL (x)			
			
	; ------------------------------------
	; TILING : tiles of 16x32
	; ------------------------------------	
	; X into BL
	mov bl, dl
	and bl, 0x0F ; x % 16		
	shr bl, 1 ; x / 2 => x = 0...7
			
	; vertical symetry every column of 16px
	test dl, 16			
	jz step2 ; if ((x & 16) != 0)
		neg bl
		add bl, 7 ; bl = 7 - bl
	step2:
	
	; Y into AL
	mov al, dh
	and al, 0x1F ; y % 32		
	shr al, 2 ; y / 4 => y = 0...7
	
	; horizontal symetry every row of 32px
	test dh, 32 ; and 32				
	jz step3 ; if ((y & 32) != 0)
		neg al
		add al, 7 ; al = 7 - al
	step3:
	
	; middle screen split : X + Y on the left side, X - Y on the right side
	dec dl
	jz right ; if X < 0
		neg bl
	right: ; X > 0
	add al, bl
	
	; ---------------------------------------
	; FPU : compute x² + y²
	; ---------------------------------------
	movsx bx, dl ; extends X into 16bits
	movsx cx, dh ; extends Y into 16bits
	
	mov [si], bx
	fild word [si] ; x
	fmul st0 ; x²
	
	mov [si], cx
	fild word [si] ; y
	fmul st0 ; y² x²
	
	fadd st0, st1 ; y²+x²		
	fistp word [fpu_target + 1] ; store result by erasing the add operand

	; composition : diagonal gradient
	add cx, bx ; x + y
	shr cx, 2 ; (x + y) / 2
	
	; get x²+y² back, add it
	fpu_target:
	mov bx, 0xAAAA ; value doesn't matter since we erase it
	and bx, 0x07 ; distance % 8
	add ax, bx ; y + distance
	and al, 0x0F ; y % 16 : stay in a 16-colors palette
	
	; time variation : scroll down
	sub cx, bp
	shr cl, 1 ; slow down time
	shl cl, 3 ; * 8
	add al, cl
	
	; ------------------------------------
	; DRAW
	; ------------------------------------					
	draw: 			
	
	stosb ; draw AL & increment DI
		
	pop cx ; restore loop counter
loop pixel
	
; <--- end of frame
inc bp ; increment time counter

; check input for ESC	
in al, 0x60 ; input from port 0x60 to AL : read keyboard
dec	al ; decrement AL by 1 : is it ESC ?
jz abort

	; .... PALETTE UPDATE & SOUND .....
	mov dx,	0x331 ;	MIDI Control Port
	mov si, music
	outsb ; UART mode
	dec dx ; MIDI Data Port (0x330)
	
	mov bx, bp	
	
	cmp bx, 256
	jle kick
	
	mov al, 40 ; snare 0x86, 0x77 11 21 25 // 19 7 33 35 39
	test bx, 0x0F
	jz drum_note
	
	kick:
	mov al, 36 ; kick  0x03, 0x11, 0x21, 0x23, 0x27, 0x29, 0x31, 0x39 // 0x03 ou 0x07 ou 0x13
	test bx, 0x33
	jz drum_note
	
	salc ; xor al, al
	
	drum_note:
	mov [si + 1], al ; drum note

	; play sounds
	mov cl, 8
	rep outsb 

	; synth
	mov [si - 2], byte 0 ; permanently mute after the first note
		
	; ..... END SOUND ....	
	darken:
	shr bx, 3 ; darken every 8 frames

	; max to 64
	cmp bl, 64
	jle next1
		mov bl, 64
	next1:
	
	; progressively augment drum volume
	mov [si - 6], byte bl

	; brighten red
	mov al, 64
	sub al, bl ; 64 -> 0
	mov [brighten_red + 2], byte al

	; go_green:
	shr bl, 5 ; brighten every 256 frames
	mov [darken_green + 2], byte bl
	
	mov cx, 0xFF ; reset loop for palette generation
	jmp palette
	
; .... END PALETTE UPDATE .....

; + ABORT SEQUENCE
abort:

mov al, 0x10 ; back to text mode
int 0x10 ; switch video mode
ret	; quit program

music:
	db 0x3F ; UART mode
    db 0x99 ; NOTE ON on channel 9
	db 44 ; note
	db 80 ; volume
    db 0xc0 ; change instrument on channel 0
	db 101 ; instrument
	db 0x90 ; NOTE ON on channel 0
    db 35 ; note
	db 80 ; volume	