;	256 AUTOMATA
;	a
;	256byte demo
;	by
;	Polaris & BarZoule
;	of the
;	Northern Dragons

;	featuring:
;	 colors, music, sync, self-exit, exit on [esc]

;	Lastest version of the source is below, so you can try
;	to figure out what's going on and maybe learn from it.

bits 16
org 100h

%define b byte
%define w word

GD_WIDTH	equ 40
GD_HEIGHT 	equ 25
GD_SIZE 	equ (GD_WIDTH*GD_HEIGHT-8)  ;//paste it to screen+8, but logically it starts at 0,0
GD_STEP_X 	equ (3)
GD_STEP_Y 	equ (GD_WIDTH*3)
PART_MAX 	equ 6
COLORIZE	equ 0
COLORIZE2	equ 1

PENTIUMPRO	equ 0

main:
;set screen mode
;mov ax,0h
int 10h
;15 bytes of code. same as 5 particles.
mov di,partD	;assume partD and partP are consecutive
mov al,GD_STEP_X
mov cl,PART_MAX
rep	stosb
mov al,4*GD_STEP_X+2*GD_STEP_Y	;00FC
mov cl,PART_MAX
rep	stosw

mov ch,02h		;mov cx,512, drives the length of the intro
mloop:
mov ax,242h		;mov ax,512+64+2, gotta be more than 64 more than CX
sub ax,cx
shr ax,6		;3	ax = (512-cntz)/64
cmp al,PART_MAX	;2	compare low only.
jl under5		;2	max of 5 particle at once
mov al,PART_MAX	;2	if(PART_NUM>=5)PART_NUM=5;
under5:
push cx

push ax			;got PART_NUM through AX

mov ch, 4h ;GD_SIZE	;undo last draw (GD_SIZE if 03e0, if we count over 400, it's plenty
%IF COLORIZE2
mov di, grid-1	;grid-1 == col
stosb			;al == particle count
%ELSE
mov di, grid	;grid-1 == col
%ENDIF
mov al,-1
rep	stosb

;//move + draw
pop cx			;retrieve PART_NUM to cx
stosb
update:
mov si,cx		; cx at this point is 5..1, hence -1 and -2
lea di,[partP-2+si]
add di,si
add si,partD-1

;//flip dir on borders
mov ax,[di]		;partP+[di]
mov dl,GD_WIDTH
div dl			;ah = ax % W  ; al = ax / W
mov dx,ax		;keep a copy
or ax, 1818h	;	((24<<8)|24)	;ah|=24 ; al|=24
cmp ah,18h
je play
mov dl,dh		;note to play in dl
cmp al,18h
jne noplay
play:
;//play note
movzx ax,dl
mov dl,3
div dl			;al/=3
push cx
xchg cx,ax		;not really safe but.. dl should always be multiple of 3 right?

;; takes bx as input
;; put mpu into uart mode
;; dx = midiport
mov dx,331h
mov al,3Fh
out dx,al

;;play a test note
dec dx
mov al,90h
out dx,al		; Note on - chanel 0
mov ax,0A53Ah	; otherwise get it in CX
%IF COLORIZE2
mov b [col],al	;colorize background, picking an ok col-scheme
%ENDIF
gnote:
shl ah,1
adc al,2
loop gnote		; done computing the note
out dx,al		; Note number
out dx,al		; Velocity

pop cx

;//bounce
neg b [si]		;partD+[si]
noplay:
;//move
movsx ax, b [si];partD+[si]		;partD is byte   ;movsx as big as mov + cbw
add [di],ax		;partP is word
;di == partP[cx]
;di = &grid[ partP[cx] ]
;bx = grid[ partP[cx] ]
mov di, [di]
add di, grid
movzx bx, b [di];bx used in full for storing AL
mov ax,cx		;counter is 1..5
dec ax
cmp bl,bh		;grid+[di]  if <0, nothing plot yet on grid
jl  keepdir		;signed compare
;//collide
mov ax, ((-GD_STEP_Y) & 0FFh) | (GD_STEP_Y << 8)
mov dl, [si]
cmp dl, ah
je xax
cmp dl, al
je xax
jmp yax
midjump:	jmp mloop	;loop mloop toward the end can't jumps that far so we use this to bounce up
xax:
mov ax, ((-GD_STEP_X) & 0FFh) | (GD_STEP_X << 8)
yax:
mov [si],ah
mov [partD+bx],al
mov al,-2
keepdir:
stosb			;mov BYTE PTR [di],al		;di is gonna be reset after loop, don't care.
loop update

xor ah,ah		;2	;seed initial value
int 1ah			;2
xchg bx,dx		;2
inc bx			;add bl,02 takes 3 bytes. two inc takes 2
inc bx
wloop:
int 1ah			;2
cmp dx,bx		;2
jbe wloop		;2

;mov cx, GD_SIZE
mov ch, 4		;GD_SIZE is 03E0h, couple bytes short of 0400h
mov si, grid	;grid is char
xor di,di		;mov di,0	;screen offset
push es
push 0B801h		;+1 to center in screen :D
pop es
fillit:
lodsb			;al = *(grid++)
cbw				;do we really have to clean it up? yes!
cmp al,0		;-1:space  -2:diamond
jl  ntbl
ndm:
xchg ax,bx
mov al,[partD+bx]
shr al,6		;hack, because of the input numbers, gives 0..4
ntbl:
add al,2		;from -2..3 to 0..5
mov bx,glyph
xlat

;	About coloring.
;	removing the coloring removes a whole 12 bytes.
;	recoloring with a constant color could also save an additional byte
;	now about coloring separately, in a deicated loop: while it looks
;	like a good idea, removing the need of division and test, it adds
;	no less than 26 bytes!!

%IF COLORIZE
ok:
xchg bx,ax
mov bh,0Eh		;white on black
;this provides some coloring... (just a pale square in the middle)
mov ax,di
mov dl,GD_WIDTH*2
div dl			;ah= ax % dl; al= ax / dl
cmp ah,48
ja ncol			;need just one compare, since it's shifted AFTER :)
;	mov bh,7Fh		;white on grey, if on the square
mov bh,[col]
ncol:
xchg ax,bx
stosw			;mov al only (not ax), to omit coloring
%ELIF COLORIZE2
ok:
;	push ax
;	mov ax,di
;	mov dh,GD_WIDTH*2
;	div dh
;	sub ah,50
;	pop ax
;	lahf
;	and ah,080h
;	stosw

stosb
mov ax,di
;	mov dx,(GD_WIDTH*2)<<8|7Eh
mov dl, GD_WIDTH*2
div dl
cmp ah,50
mov al,[col]
%IF PENTIUMPRO
cmovge ax,di	;crashes on DOSBOX
%ELSE
jnge skip		;one byte too large :(
mov ax,di
skip:
%ENDIF
stosb
%ELSE
ok:
stosb
inc di
%ENDIF
loop fillit
pop es

pop cx
in al,60h
dec al
loopnz midjump	;mloop is too far, gotta use a midjump
;restore screen mode
mov ax,3h
int 10h
ret				;Terminate not so clean, but it's cheap

;keping data in the same section to prevent byte wasting alignment

;super precise order. we shr 6 the input and it gives us the index.
;only posible with these number, otherwise we'd have to find other masks or something :)
;03h -> 10h		00000011
;78h -> 1Fh		01111000
;88h -> 1Eh		10001000
;FDh -> 11h		11111101
glyph	db 04,32
db 16,31,30,17
;padding	dw 444Eh ;last word for ND ;)

partD	resb PART_MAX
partP	resw PART_MAX
%IF COLORIZE || COLORIZE2
col		resb 1
%ENDIF
grid	resw GD_SIZE