; + 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	; init
pop es

xor bp, bp ; time counter

; + MAIN LOOP
main:

	; + DRAW LOOP
	fragment: ; will draw the whole screen, pixel per pixel		
	push cx ; save loop counter
	
	; get coordinates : X into DX (0 - 320), Y into AX (0 - 200) 
	mov ax, di
	mov bx, 320
	xor dx, dx
	div bx
	
	; horizontal and vertical scrolling
	sub ax, bp
	add dx, bp
							
	; save coords
	mov [wtf], dx ; save screen X
	
	; divide screen Y / 16 : texelY into DX (0 - 12), tileY into AX (0 - 11)
	mov bx, 16
	xor dx, dx
	div bx
	
	push dx ; texel Y
	push ax ; texel Y, tile Y
					
	; divide screen X / 16 : texelX into DX (0 - 20), tileX into AX (0 - 19)
	mov ax, [wtf] ; read back screen X
	mov bx, 16
	xor dx, dx
	div bx
	
	mov [wtf], dl ; save texel X
	
	; compute a tile ID for each tile : 20 * 12 tiles = 240 tiles total
	inc ax ; tile X += 1
	mov [wtf+1], al ; save tileID
	
	pop bx ; tile Y
	inc bx ; tileY += 1
	
	mul bx ; tileID = (tileX + 1) * tileY
	and ax, 13 ; or 5, 11, 21, ... for other interesting patterns
	
	; do we draw a sprite or background ?		
	cmp al, 1 ; if tile ID & 13 != 1 : don't draw a sprite
	
	; background : get color from previous frame's pixel in the bottom-right corner of this pixel
	mov al, [es:di + 321]
		
	jne draw
			
	; <- drawing a sprite from here
		
		; 8 * Y + X : 0-128
		pop ax ; texel Y
		shl al, 3 ; AL *= 8
		; AL = 8 * Y
				
		; push garbage so stack length = 1 with or without entering previous if block
		push ax
		
		mov bl, [wtf] ; read back texelX
		
		; use symmetry to construct the right half
		cmp bl, 8 ; if BL >= 8
		jl after_symmetry
			
			; texelX = 15 - texelX
			mov dl, 15
			sub dl, bl
			mov bl, dl
					
		after_symmetry:
		
		add al, bl ; AL (texel) : 0 - 128
		
		; 3bits per pixel : a 2-bytes register holds 5 texels
		; compute bit offset : texel / 5
		aam 5 ; divide AL by 5 and store quotient into AH, remainder into AL
		mov cl, al
		; AH: 0-25 AL: 0-4, 
		
		shl ah, 1 ; multiply by 2 : 2 bytes (16 bits) read each time
		; we will read and address between 0 and 50
		
		; read 16bits at computed address		
		movsx bx, ah
		mov ax, word [sprite + bx]
		; AX contains 16 bits, we need to keep only the 3 bits for this pixel's palette index
		
		; texel >> (3 * DX), since its 3 bits per pixel
		shr ax, cl
		shr ax, cl
		shr ax, cl
				
		; AND 0x07 : keep first three bits
		and al, 7
		
		; palette
		shl al, 1 ; multiply by 2 the palette offset to improve color gradient
		add al, 16 ; add 16 so we start at the start of the VGA black-white gradient
			
		; use a time-varying and tile-varying color for pixels above palette color 26			
		cmp al, 26
		jl draw ; if AL >= 26
			
			add ax, bp
			sub ax, [wtf+1] ; horizontal color wave
	
		draw: 
		
		pop dx ; stack length is always 1 at this step, clear it
		stosb ; draw AL & increment DI
		pop cx ; restore loop counter
		
	loop fragment
	
	; .... SOUND .....	
	push bp ; save timer
	
	test bp, word 1 ; play sound every 2 frames
	jnz loop_end

		and bp, 15 ; AND 4 bits : 8 notes [ 0, 2, 4, 6, 8, 10, 12, 14 ]		
		
		; mov al, 0 ; default note is silence - somewhat we don't need to reset AL to 0 :)		
		jz play ; skip first note (0)
			cmp bp, 8 
			je play ; skip 4th note (8)
			
				mov al, 44 ; hi hat everywhere else
				
				cmp bp, 14 ; 8th note is a snare
				jne play
					mov al, 38 ; snare
	
	play:
	
	mov [note], al ; erase address with selected note

	mov dx,	0x331 ;	MIDI Control Port
	mov si, music_init
	outsb ; output byte at SI to port, and increment SI by 1
	dec dx ; MIDI Data Port (0x330)

	; send MIDI note commands
	outsb
	outsb
	outsb
	
	; ..... END SOUND ....
		
	loop_end:
	
	pop bp ; restore timer
	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 ?	
	
jnz main		; go back to frame drawing if AL is > 0

; + ABORT SEQUENCE

; sounds off
dec dx ; 0x331 -> 0x330
mov si, music_stop
outsb
outsb
outsb

mov al, 0x10 		; back to text mode
int 0x10			; switch video mode
ret					; quit program


music_init:
	db 0x3F ; UART mode
	db 0x99 ; NOTE ON on channel 0
note:
	db 0 ; note
	db 100 ; volume
sprite:
	dw 0x0247, 0x7110, 0x4291, 0x2486, 0x0FA1, 0x425A, 0x163E, 0x694B, 0x14C8, 0x0D2E, 0x6E63, 0x5855, 0x2DE1, 0x53A1, 0x4374, 0x78BA, 0x28C8, 0x1254, 0x551A, 0x2306, 0x04A3, 0x3461, 0x1E24, 0x124B, 0x0047
	; db 0x00 - not needed :)
	
music_stop:
	db 0xB0
	db 123
	; db 0 - not needed again, it's not like something could be written after this address ... right ? :)
		
wtf: