;----------------------------------------------------------------------------
;	ASM raytracer (C) 1999 by Sami Ky”stil„
; This code is released under the GNU GPL. See http://www.gnu.org for details.
;----------------------------------------------------------------------------

.model tiny
.386
.387

;----------------------------------------------------------------------------
;	Segmentation:
;
;	CS+1000h		rbuffer (80*25 bytes)
; CS+2000h		framebuffer (64000 bytes)
; CS+3000h		stars (? bytes)
;
;----------------------------------------------------------------------------

INT_VIDEO		= 10h
INT_DOS			= 21h
PTR_VIDEO		= 0a000h

COMPOVERSION = 0

SPHERE_RENDER		= 1
SPHERE_REFLECT	= 2
SPHERE_SHADOW		= 4

FLAG_DONE				= 1
FLAG_RAYCAST		= 2
FLAG_TEXT				= 4
;FLAG_FUSS				= 8
FLAG_STARS			= 16
FLAG_MOTIONBLUR	= 32
FLAG_CLEAR			= 64

TEXT_LF			= 129
TEXT_SLANT	= 130
TEXT_COLOR	= 131
TEXT_RASTER	= 132

RECURSIONS = 1

.DATA

rbufferseg dw ?
fbufferseg dw ?
starseg dw ?

romfont dd ?
oldtimer dd ?
demoflags db ?
tick dd 0

textptr dw ?

VECTOR3D	struc
	x	dd	?
	y	dd	?
	z	dd	?
VECTOR3D	ends

VECTOR2D	struc
	x	dd	?
	y	dd	?
VECTOR2D	ends

SPHERE	struc
	pos			VECTOR3D	?			; must be first!
	radius	dd 				?
	flags		db				?
SPHERE	ends

STAR	struc
	pos		VECTOR3D	?
	vel		VECTOR3D	?
	col		db 				?
STAR	ends


HALF_SCREEN dd 160.0

ambient_light dd 20.0

spheres db 2
spherelist:

;pallo		SPHERE	?
pallo:
dd -10.0
dd 3.0
dd 1.0
dd 1.0
db SPHERE_RENDER+SPHERE_REFLECT

;lattia	SPHERE	?
lattia:
dd 8.0
dd 10.0
dd -10001.0
dd 10000.0
db SPHERE_RENDER+SPHERE_SHADOW

aurinko:
dd 141.0
dd 180.3
dd 100.0
dd 30.0
db SPHERE_RENDER

;vuori1	SPHERE	?
vuori1:
dd -8.0
dd 21.0
dd 0.0
dd 8.0
db SPHERE_RENDER+SPHERE_REFLECT

;vuori2	SPHERE	?
vuori2:
dd 12.3
dd 21.0
dd 0.0
dd 16.0
db SPHERE_RENDER+SPHERE_REFLECT

pallo2:
dd -1.3
dd 3.0
dd 1.0
dd 1.2
db SPHERE_RENDER+SPHERE_REFLECT

stars dw ?

SMALLNUM dd 1e-8
BIGNUM dd 1e30
pallo_left dd -8.0
two  dd 2.0
four dd 4.0
half dd 0.5
quarter dd 0.25
tenth dd 0.1
shadow dd -22.0
hundred dd 100.0
initial_tmin dd 1e30
G dd 0.001
ground dd 0.0
ballspeed dd 0.03
cameraspeed dd 0.08

tmin dd ?				
light VECTOR3D <-10.0,-8.3,10.0>
zero VECTOR3D <0.0,0.0,0.0>

retvec VECTOR3D ?
retfloat dd ?
tempv VECTOR3D ?

i32 dd ?
i8  db ?
sx dw ?
sy dw ?

camera_x dd 0.0
camera_y dd 0.0
camera_z dd 0.0


introtext db TEXT_RASTER,1,'The',TEXT_LF,32,'Answer',TEXT_RASTER,0,TEXT_LF,110,TEXT_LF,0,TEXT_SLANT,1,TEXT_COLOR,60,'ASM',39,TEXT_COLOR,100,'99',TEXT_LF,100,'4k',TEXT_COLOR,60,' intro',0

text1 db 'A humble',TEXT_LF,110,'figure',TEXT_LF,140,' is making',TEXT_LF,128,'his way',0,TEXT_LF,100,'across',0
text2 db TEXT_LF,0,' across',TEXT_LF,100,' the',TEXT_LF,86,'Wasteland',0
text3 db ' He is',TEXT_LF,86,TEXT_SLANT,1,TEXT_COLOR,150,'Nergut,',TEXT_COLOR,100,TEXT_SLANT,0,TEXT_LF,120,'the one',TEXT_LF,120,'who seeks',TEXT_LF,153,TEXT_COLOR,200,'the Answer',0
text4 db ' He has',TEXT_LF,120,'traversed',TEXT_LF,130,'through',TEXT_LF,130,'the ',TEXT_COLOR,150,'G',39,'Gok',TEXT_COLOR,100,TEXT_LF,136,'Valley...',0
text5 db 'and over',TEXT_LF,100,'the',TEXT_LF,76,'Crystal',TEXT_LF,120,'Mountains',TEXT_LF,130,'of Yoth.',0
text6 db 'Finally',TEXT_LF,80,'he',TEXT_LF,60,'reaches',TEXT_LF,132,'the Guru',39,'s',TEXT_LF,114,'hut.',0
text7 db 'My son,',TEXT_LF,127,'the Answer',TEXT_LF,150,'is...umm',TEXT_LF,100,'Darn!',TEXT_LF,110,TEXT_RASTER,1,'I forgot!',0

IF COMPOVERSION
text8 db 'Nergut',39,'s',TEXT_LF,120,'voyage',TEXT_LF,114,'continues',TEXT_LF,100,'...',0
ELSE
text8 db 'Nergut',39,'s',TEXT_LF,120,'voyage',TEXT_LF,114,'continues',TEXT_LF,100,'...',TEXT_LF,95,TEXT_COLOR,60,'by hiteck',0
ENDIF

.CODE
org 100h
start:
	jmp main

;----------------------------------------------------------------------------
; RANDOM
;
; Returns a pseudorandom number between in the range of dx-cx in ax.
;
;----------------------------------------------------------------------------
randseed dw 31337
random:
	push bx
	push cx
	push dx

	mov ax, randseed
	mov bx, 1021
	mul bx      
	inc ax      
	mov bx, 32768
	div bx	    
	mov randseed, dx
	mov ax, dx      
	xor dx, dx      
	mov bx, dx
	cmp bx, cx      
	jg  swap
resume:
	inc cx        
	sub cx, bx    
	div cx        
	mov ax, bx    
	add ax, dx     
	jmp randdone
swap:
	xchg bx, cx
	jmp resume

randdone:
	pop dx
	pop cx
	pop bx
ret ; random


;----------------------------------------------------------------------------
; PRINTTEXT
;
;	Prints text into the virtual frame buffer.
;
; al:			text color
; ds:si:	pointer to a null terminated string.
;
;----------------------------------------------------------------------------
slant dw 0
raster db 0
lfnumber db 0
lfcount db ?
printtext:
	push ds
	push ax
	push bx
	push cx
	push dx

	mov cx, word ptr romfont+2

	mov bp, ds

	mov dl, al
	shl edx, 8
	mov dl, al
	shl edx, 8
	mov dl, al
	shl edx, 8
	mov dl, al

	mov di, 16*320+32

	mov raster, 0
	mov slant, 0
	mov lfcount, 0

cloop:
	xor bx, bx

	xor eax, eax
	mov al, byte ptr ds:[si]
	test al, 128
	jnz controlchar

	test al, al
	jz pois

	add bx, ax
	shl bx, 4

	add bx, cx

	mov ah, 16

fyloop:
	mov es, word ptr romfont
	mov al, byte ptr es:[bx]
	mov es, fbufferseg

	test al, 128
	jz f_no1
	mov dword ptr es:[di], edx
	cmp raster, 0
	jnz f_no1
	mov dword ptr es:[di+320], edx
f_no1:
	test al, 64
	jz f_no2
	mov dword ptr es:[di+4], edx
	cmp raster, 0
	jnz f_no2
	mov dword ptr es:[di+4+320], edx
f_no2:
	test al, 32
	jz f_no3
	mov dword ptr es:[di+8], edx
	cmp raster, 0
	jnz f_no3
	mov dword ptr es:[di+8+320], edx
f_no3:					 
	test al, 16
	jz f_no4
	mov dword ptr es:[di+12], edx
	cmp raster, 0
	jnz f_no4
	mov dword ptr es:[di+12+320], edx
f_no4:
	test al, 8
	jz f_no5
	mov dword ptr es:[di+16], edx
	cmp raster, 0
	jnz f_no5
	mov dword ptr es:[di+16+320], edx
f_no5:
	test al, 4
	jz f_no6
	mov dword ptr es:[di+20], edx
	cmp raster, 0
	jnz f_no6
	mov dword ptr es:[di+20+320], edx
f_no6:
	test al, 2
	jz f_no7
	mov dword ptr es:[di+24], edx
	cmp raster, 0
	jnz f_no7
	mov dword ptr es:[di+24+320], edx
f_no7:
	test al, 1
	jz f_no8
	mov dword ptr es:[di+28], edx
	cmp raster, 0
	jnz f_no8
	mov dword ptr es:[di+28+320], edx
f_no8:

	add di, 640
	sub di, slant
	inc bx
	dec ah
	jnz fyloop

	sub di, (639*16)-8
	mov ax, slant
	shl ax, 4
	add ax, 8
	add di, ax

	inc si	
	jmp cloop

controlchar:
	cmp al, TEXT_LF
	jnz no_lf

	inc lfcount
	mov al, lfnumber
	cmp lfcount, al
	ja pois

	inc si
	mov al, byte ptr ds:[si]
	shl ax, 1
	add di, 320*32
	sub di, ax
no_lf:
	cmp al, TEXT_SLANT
	jnz no_slant
	inc si
	mov al, byte ptr ds:[si]
	mov slant, ax
no_slant:
	cmp al, TEXT_COLOR
	jnz no_color
	inc si
	mov al, byte ptr ds:[si]
	mov dl, al
	shl edx, 8
	mov dl, al
	shl edx, 8
	mov dl, al
	shl edx, 8
	mov dl, al
no_color:
	cmp al, TEXT_RASTER
	jnz no_raster
	inc si
	mov al, byte ptr ds:[si]
	mov raster, al
no_raster:
	inc si
	jmp cloop

pois:
	pop dx
	pop cx
	pop bx
	pop ax
	pop ds
ret ; printtext

;----------------------------------------------------------------------------
;	HOOKTIMER
;
; Hooks the int8 vector.
;
;----------------------------------------------------------------------------
hooktimer:
	cli
	mov ax, 3508h
	int INT_DOS
	mov word ptr oldtimer, es
	mov word ptr oldtimer+2, bx
	
	mov dx, offset kernel
	
	mov ax, 2000h

	mov al,36h
	out 43h,al
	mov al,0
	out 40h,al
	mov al,ah
	out 40h,al
	mov ax, 2508h
	int INT_DOS
	sti
ret ; hooktimer

;----------------------------------------------------------------------------
; UNHOOKTIMER
;
; Restores the int8 vector.
;
;----------------------------------------------------------------------------
unhooktimer:
	push ds
	push ax
	cli
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov dx, word ptr es:oldtimer+2
	mov ds, word ptr es:oldtimer
	
	mov ax, 2508h
	int 21h
	sti
	pop ax
	pop ds
ret ; unhooktimer

;----------------------------------------------------------------------------
;	DOT
;
;	Returns the dot product of two vectors in retfloat.
;
;	bx:		pointer to vector 1
;	di:		pointer to vector 2
;
;	double dot(v3 v1, v3 v2)
;		return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
;
;	Trashes:
; 	retfloat
;----------------------------------------------------------------------------
dot:
	fld [bx + VECTOR3D.x]		; x1
	fld [di + VECTOR3D.x]	  ; x2:x1
	fmulp							 			; (x2*x1)

	fld [bx + VECTOR3D.y]		; y1:(x2*x1)
	fld [di + VECTOR3D.y]		; y2:y1:(x2*x1)
	fmulp							 			; (y2*y1):(x2*x1)

	fld [bx + VECTOR3D.z]		; z1:(y2*y1):(x2*x1)
	fld [di + VECTOR3D.z]		; z2:z1:(y2*y1):(x2*x1)  
	fmulp							 			; (z2*z1):(y2*y1):(x2*x1)

	faddp										; (z2*z1)+(y2*y1):(x2*x1)
	faddp										; (z2*z1)+(y2*y1)+(x2*x1)

	fstp retfloat
ret

;----------------------------------------------------------------------------
; COMBINE
;
; Adds scaled vector A into vector B and returns the result in retvec.
;
; retfloat:	magnitute (floating point)
;	bx: 	pointer to vector A
; di: 	pointer to vector B
;
;	v3 combine(v3 v1, double a, v3 v2)
;		v2.x += a * v1.x;
;		v2.y += a * v1.y;
;		v2.z += a * v1.z;
;		return v2;
;
; Trashes:
; 	retvec
;----------------------------------------------------------------------------
combine:
	fld retfloat
	fld [bx + VECTOR3D.x]
	fmul st(0),st(1)
	fadd [di + VECTOR3D.x]
	fstp retvec.x

	fld [bx + VECTOR3D.y]
	fmul st(0),st(1)
	fadd [di + VECTOR3D.y]
	fstp retvec.y

	fld [bx + VECTOR3D.z]
	fmul st(0),st(1)
	fadd [di + VECTOR3D.z]
	fstp retvec.z
	fstp st(0)
ret

;----------------------------------------------------------------------------
; VECTADD
;
; Adds scaled vector A into vector B and returns the result in retvec.
;
;	bx: 	pointer to vector A
; di: 	pointer to vector B
;
; Trashes:
; 	retvec
;----------------------------------------------------------------------------
vectadd:
	fld [bx + VECTOR3D.x]
	fadd [di + VECTOR3D.x]
	fstp retvec.x

	fld [bx + VECTOR3D.y]
	fadd [di + VECTOR3D.y]
	fstp retvec.y

	fld [bx + VECTOR3D.z]
	fadd [di + VECTOR3D.z]
	fstp retvec.z
ret

;----------------------------------------------------------------------------
; VECTSUB
;
; Subtracts scaled vector B from vector A and returns the result in retvec.
;
;	bx: 	pointer to vector A
; di: 	pointer to vector B
;
; Trashes:
; 	retvec
;----------------------------------------------------------------------------
vectsub:
	fld [bx + VECTOR3D.x]
	fsub [di + VECTOR3D.x]
	fstp retvec.x

	fld [bx + VECTOR3D.y]
	fsub [di + VECTOR3D.y]
	fstp retvec.y

	fld [bx + VECTOR3D.z]
	fsub [di + VECTOR3D.z]
	fstp retvec.z
ret


;----------------------------------------------------------------------------
; NORM
;
; Normalizes vector pointed to by bx.
;
;	v3 norm(v3 vec)
;		return combine(vec, 1.0/sqrt(dot(vec,vec)), zero);
;
; l = 1.0/sqrt(bx.x*bx.x + bx.y*bx.y + bx.z*bx.z)
;	bx.x = bx.x * l
;	bx.y = bx.y * l
;	bx.z = bx.z * l
;----------------------------------------------------------------------------
norm:
	fld [bx + VECTOR3D.x]
	fmul st(0),st(0)
	fld [bx + VECTOR3D.y]
	fmul st(0),st(0)
	fld [bx + VECTOR3D.z]
	fmul st(0),st(0)

; 0   1   2  
; x*x y*y z*z
	
	faddp
	faddp

; 0
; x*x+y*y+z*z

	fsqrt

; 0
; sqrt(x*x+y*y+z*z)

	fld1
	fdivr

; 0
; 1.0/sqrt(x*x+y*y+z*z)

	fld [bx + VECTOR3D.x]
	fmul st(0), st(1)
	fstp [bx + VECTOR3D.x]
	fld [bx + VECTOR3D.y]
	fmul st(0), st(1)
	fstp [bx + VECTOR3D.y]
	fld [bx + VECTOR3D.z]
	fmul st(0), st(1)
	fstp [bx + VECTOR3D.z]

	fstp st(0)
ret

;----------------------------------------------------------------------------
; INTERSECT
;
; Returns a pointer to the sphere the ray intersects with in ax.
;
;	bx: VECTOR3D pointer to ray origin
;	di: VECTOR3D pointer to ray vector
;	dx: pointer to a sphere to ignore.
;
; Trashes:
;
;		i8, retvec, retfloat
;----------------------------------------------------------------------------

s dw ?
a dd ?
b dd ?
t dd ?

intersect:
	push ecx
	push ebx

	mov cx, offset pallo
	mov s, cx

	mov ecx, initial_tmin
	mov tmin, ecx

	mov cl, spheres
	mov i8, cl

	mov ax, 0

	; a = rayvec.x^2 + rayvec.y^2 + rayvec.z^2;

	fld [di + VECTOR3D.x]		; x
	fmul st(0), st(0)

	fld [di + VECTOR3D.y]		; y x*x
	fmul st(0), st(0)

	fld [di + VECTOR3D.z]		; z y*y x*x
	fmul st(0), st(0)

	faddp 									; z*z+y*y x*x
	faddp 									; z*z+y*y+x*x

	fstp a

iloop:
	cmp dx, s
	jz next_iloop

	; solve t, when at^2 + bt + c = 0

	; retvec = difference between sphere and ray origins

	mov cx, di
	mov di, s

	test [di + VECTOR3D.flags], SPHERE_RENDER
	jnz sphere_ok
	mov di, cx
	jmp next_iloop
sphere_ok:

	fld [di + VECTOR3D.x]	 
	fsubr [bx + VECTOR3D.x]
	fstp retvec.x

	fld [di + VECTOR3D.y]	 
	fsubr [bx + VECTOR3D.y]
	fstp retvec.y

	fld [di + VECTOR3D.z]	 
	fsubr [bx + VECTOR3D.z]
	fstp retvec.z

	mov di, cx

	;	b=2.0 * dot(rayvec, v);
	;	bx:		pointer to retvec
	;	di:		pointer to rayvec

	push ebx
	mov bx, offset retvec
	call dot
	fld retfloat
	fld two
	fmulp
	fstp b
	pop ebx

	; now we have a, b and retvec solved.

	; discriminant: b*b - 4*a*c
	
	; t = b*b - 4*a*(dot(v,v) - (s->radius*s->radius));

	push edi
	push ebx
	mov di, offset retvec
	mov bx, s

	fld [di + VECTOR3D.x]			; x
	fmul st(0), st(0)

	fld [di + VECTOR3D.y]			; y x*x
	fmul st(0), st(0)

	fld [di + VECTOR3D.z]			; z y*y x*x
	fmul st(0), st(0)

	faddp											; z*z+y*y x*x
	faddp											; z*z+y*y+x*x

	fld [bx + SPHERE.radius]	; r z*z+y*y+x*x
	fmul st(0), st(0)
	fsubp											; (z*z+y*y+x*x)-(r*r)

	fld a											
	fmul four
	fmulp											; 4*a*((z*z+y*y+x*x)-(r*r))

	fld b
	fmul st(0),st(0)
	fsubrp										; (b*b)-(4*a*((z*z+y*y+x*x)-(r*r)))

	pop ebx
	pop edi
	
	; now we have the discriminant squared in st(0)

	push eax
	fcom SMALLNUM
	fstsw ax
	sahf
	pop eax
	jc next_iloop_with_fpop	; no hit?

	; two real roots - get the nearest

	fsqrt											; sqrt(t)	
	
	; t = (-b ñ sqrt(t)) / 2a

	fld b
	fchs 											; -b sqrt(t)
	
	fld st(1)
	fld st(1)									; -b sqrt(t) -b sqrt(t)

	faddp											; -b+sqrt(t) -b sqrt(t)
	fxch											; -b -b+sqrt(t) sqrt(t)

	fsubrp st(2),st(0)				; -b+sqrt(t) -b-sqrt(t)

	push eax
	fcom st(1)
	fstsw ax
	sahf
	pop eax
	jnc st1_smaller
	fxch
st1_smaller:
	fstp st(0)								; t

	fmul half									; 0.5*t
	fdiv a										; t/2a

	; now we have the final t value in st(0)

	push eax
	fcom SMALLNUM
	fstsw ax
	sahf
	pop eax
	jc next_iloop_with_fpop

	push eax
	fcom tmin
	fstsw ax
	sahf
	pop eax
	jnc	next_iloop_with_fpop

	fstp tmin									; found intersection!
	mov ax, s

next_iloop_with_fpop:
	fstp st(0)

next_iloop:
	add s, size SPHERE
	dec i8
	jnz iloop

break_iloop:
	pop ebx
	pop ecx
ret	; intersect

;----------------------------------------------------------------------------
; TRACE
;
; Traces a ray and returns the final color in eax. (must be saturated)
;
;	bx: VECTOR3D pointer to ray origin
;	di: VECTOR3D pointer to ray vector
;	ax: pointer to a sphere to ignore.
; cx: recursion
;
;----------------------------------------------------------------------------
v VECTOR3D ?
color dd ?
intpnt VECTOR3D ?
temp_s dw ?
rcos1 dd ?
rcos2 dd ?
org_rayvec dw ?
ir dd 2.0

trace:
	push ecx
	push edx
	push edi

	xor dx, dx

	cmp cx, RECURSIONS
	ja trace_abort

	mov org_rayvec, di

	call intersect							; sphere hit in ax

	cmp ax,0
	jz trace_abort

	mov temp_s, ax

	mov eax, tmin
	mov retfloat, eax

	xchg bx, di

;	mov bx, s + SPHERE.pos

	call combine

	; now the intersection point is in retvec

	mov eax, retvec.x
	mov intpnt.x, eax
	mov eax, retvec.y
	mov intpnt.y, eax
	mov eax, retvec.z
	mov intpnt.z, eax

;	bx: VECTOR3D pointer to ray origin
;	di: VECTOR3D pointer to ray vector
;	dx: pointer to a sphere to ignore.

	; trace shadows
	mov bx, temp_s
	test [bx + SPHERE.flags], SPHERE_SHADOW
	jz no_shadow

	mov bx, offset intpnt
	mov di, offset light
	xor dx, dx

	call intersect
	cmp ax, 0
	jz no_shadow
;	fild color
	fld shadow
;	fchs
	fistp color
	no_shadow:

;	fld intpnt.y
;	fiadd color
;	fistp color

	; reflection

	mov bx, temp_s ;+ SPHERE.pos

	mov di, offset intpnt
	call vectsub
	mov bx, offset retvec
	call norm

	mov bx, offset zero
	mov di, offset retvec
	call vectsub

	mov eax, retvec.x
	mov intpnt.x, eax
	mov eax, retvec.y
	mov intpnt.y, eax
	mov eax, retvec.z
	mov intpnt.z, eax

	mov bx, temp_s
	test [bx + SPHERE.flags], SPHERE_REFLECT
	jz no_reflection

	push color
	push temp_s
	mov di, offset intpnt
	mov dx, temp_s
;	xor dx, dx
	inc cx

	call trace
	pop temp_s
	fild color
	pop color

	fdiv four
	fiadd color
	fistp color

no_reflection:

	; now intpnt contains the normal

	; highlight

	mov bx, offset intpnt
	mov di, offset tempv
	call dot
	fld retfloat
	fchs
	fstp rcos1						; -dot(normal,rayvec)

	fld1
	fld rcos1
	fmul st(0),st(0)
	fsubp

	fld ir
	fmul st(0),st(0)
	fmulp
	fld1
	fsubrp
	
	fmul ambient_light
;	fadd four
	fiadd color
	fistp color

	; refraction

	mov bx, offset intpnt
	mov di, offset light
	call dot
	fld retfloat

	fcom zero.x
	fstsw ax
	sahf
	jnc shadow_ok
	fstp st(0)
	fld zero.x
shadow_ok:

	fmul two
	fadd hundred
	fiadd color
	fistp color

trace_abort:
	pop edi
	pop edx
	pop ecx

	mov eax, color

	ret ; trace

pre_y dd ?

;----------------------------------------------------------------------------
; RENDER
;
;	Prepares the rbuffer.
; 
;----------------------------------------------------------------------------
ehm dd 117.473471

render:
	xor ax, ax
	mov sy, ax
	mov di, ax

	mov ax, rbufferseg
	mov es, ax

yloop:
	xor ax, ax
	mov sx, ax
xloop:
	fild sx					; sx
	fld HALF_SCREEN	; 160.0 sx
	fsubp						; sx-160.0
	fadd camera_x
	fstp tempv.x

	fld hundred
	fild sy
	fsubp
	fadd camera_z
	fstp tempv.z

	mov eax, ehm
	mov tempv.y, eax
	fld tempv.y
	fadd camera_y
	fstp tempv.y
	mov bx, offset tempv
	call norm

	push di
	mov edx, 0
	mov color, edx
	mov bx, offset zero
  mov di, offset tempv
	mov cx, 0
	call trace

	cmp color, 254
	jbe ok
	mov color, 254
	ok:
	cmp color, 0
	ja ok2
	mov color, 0
	ok2:
	mov eax, color

	pop di

	stosb

	add sx, 4
	cmp sx, 320
	jb xloop

	add sy, 4
	cmp sy, 200
	jb yloop
	
ret ; trace

;----------------------------------------------------------------------------
; RENDER_HLINE
;
;	Bilerps a horizontal scanline.
;
;	dl Û²±° bl
;
;	dl/bl - left/right
;	ds:si - address
;
;----------------------------------------------------------------------------
render_hline:
	push ecx
	push eax

	mov byte ptr [si], dl
	mov byte ptr [si+3], bl

	mov al, dl
	shr al, 1
	mov ah, bl
	shr ah, 1
	add al, ah

	mov cl, al

	shr al, 1
	mov ah, dl
	shr ah, 1
	add al, ah
	mov byte ptr [si+1], al

	shr cl, 1
	mov ch, bl
	shr ch, 1
	add cl, ch
	mov byte ptr [si+2], cl

	pop eax
	pop ecx
ret ; render_hline

;----------------------------------------------------------------------------
; RENDER_RBUFFER
;
; Renders the scanline buffer prepared by `render'.
;
;	Kills just about every register.
;
;----------------------------------------------------------------------------
no_mask db 1
render_rbuffer:
	push ds
	push gs
	xor ax, ax
	mov sy, ax

	mov ax, ds
	mov gs, ax

	mov ax, rbufferseg
	mov es, ax

	mov ax, fbufferseg
	mov ds, ax
	
	xor di, di
	xor si, si

	xor bl, bl

yloop1:
	xor bh, bh
xloop1:
	mov ax, word ptr es:[di]

	mov byte ptr [si], al

	mov ch, ah

	mov cl, al
	shr cl, 1
	shr ah, 1
	add cl, ah
	mov byte ptr [si+2], cl

	mov dl, cl
	shr dl, 1
	shr al, 1
	add dl, al
	mov byte ptr [si+1], dl

	mov dl, cl
	shr dl, 1
	shr ch, 1
	add dl, ch
	mov byte ptr [si+3], dl

	inc di
	add si, 4

	inc bh
	cmp bh, 80
	jb xloop1

	sub di, 80

	mov cx, 80
yloop2:
	push ebx
	push ecx
					
	;	 al	    cl
	;		ÚÄÄÄÄÄ¿
	;		³Û²±°.³
	;		³²±°..³
	;		³±°...³
	;		³°....³
	;		³.....³
	;		ÀÄÄÄÄÄÙ
	;	 ah		  ch
	mov al, byte ptr es:[di]
	mov ah, byte ptr es:[di+80]

	mov cl, byte ptr es:[di+1]
	mov ch, byte ptr es:[di+81]

	test gs:no_mask, 255
	jnz render_rbuffer_no_mask
	mov dx, ax
	add dx, cx
	jz blank
render_rbuffer_no_mask:

	mov dl, al
	shr dl, 1
	mov dh, ah
	shr dh, 1
	add dl, dh

	mov bl, cl
	shr bl, 1
	mov bh, ch
	shr bh, 1
	add bl, bh

	mov dh, dl
	mov bh, bl

	add si, 320
	call render_hline
	sub si, 320

	shr dl, 1
	shr bl, 1
	shr al, 1
	shr cl, 1
	add dl, al
	add bl, cl
	call render_hline

	add si, 640
	shr dh, 1
	shr bh, 1
	shr ah, 1
	shr ch, 1
	add dh, ah
	add bh, ch
	mov dl, dh
	mov bl, bh
	call render_hline
	sub si, 640

blank:
	add si, 4
	inc di

	pop ecx
	pop ebx

	dec cx
	jnz yloop2

	add si, 320+320
	inc bl
	cmp bl, 49
	jb yloop1

	pop gs
	pop ds
ret ; render_rbuffer

;----------------------------------------------------------------------------
;	DO_FAKEG
;
;	Does a hyperbolic gravity approximation.
;
;	bx:	pointer to sphere
;
;----------------------------------------------------------------------------

delta dd 0.0
do_fakeg:
	fld [bx + SPHERE.pos.z]
	fsub delta
	fstp [bx + SPHERE.pos.z]

	fld delta
	fadd G
	fstp delta

	push eax
	fld [bx + SPHERE.pos.z]
	fcomp ground
	fstsw ax
	sahf
	pop eax
	jnc	no_bounce

	fld delta
	fchs
	fstp delta
	
	no_bounce:	
ret ; do_fakeg

;----------------------------------------------------------------------------
;	SETCOLOR
;
; Sets palette index al to RGB(ah,bl,bh)
;
;----------------------------------------------------------------------------
setcolor:
	push ax
;	push di
	push dx
;	push ds

;	xor di, di
;	mov ds, palseg

;	mov di, ax
;	and di, 255
;	shl di, 2
;	mov byte ptr ds:[di+0], ah
;	mov byte ptr ds:[di+1], bl
;	mov byte ptr ds:[di+2], bh

	mov dx, 03c8h
	out dx, al
	inc dx
	
	mov al, ah
	out dx, al
	mov al, bl
	out dx, al
	mov al, bh
	out dx, al

;	pop ds
	pop dx
;	pop di
	pop ax
ret ; setcolor

;----------------------------------------------------------------------------
;	SETBLUEPALETTE
;
; Guess.
;
; Regs destroyed.
;
;----------------------------------------------------------------------------
setbluepalette:
	xor ax, ax
	xor bx, bx

	mov cx, 63

p1:
	inc al

	call setcolor
	inc al
	call setcolor

	inc bh

	dec cx
	jnz p1

	mov cx, 63

p2:
	inc bl
	inc ah

	inc al
	call setcolor
	inc al
	call setcolor

	dec cx
	jnz p2
ret ; setbluepalette

;----------------------------------------------------------------------------
;	SETREDPALETTE
;
; Guess.
;
; Regs destroyed.
;
;----------------------------------------------------------------------------
setredpalette:
	xor ax, ax
	xor bx, bx

	mov cx, 63

p3:
	inc al
	inc ah
	mov bh, ah
	shr bh, 1
	mov bl, bh

	call setcolor
	inc al
	call setcolor

	dec cx
	jnz p3

	mov cx, 32

p4:
	inc al

	inc bl

	call setcolor
	inc al
	call setcolor
	inc al
	call setcolor
	inc al
	call setcolor

	dec cx
	jnz p4
ret ; setredpalette

;----------------------------------------------------------------------------
;	SETYELLOWPALETTE
;
; Guess.
;
; Regs destroyed.
;
;----------------------------------------------------------------------------
setyellowpalette:
	xor ax, ax
	xor bx, bx

	mov cx, 63

p5:
	inc al
	inc ah
	mov bl, ah
	shr bl, 1

	call setcolor
	inc al
	call setcolor

	dec cx
	jnz p5

	mov cx, 32

p6:
	inc al

	inc bl

	call setcolor
	inc al
	call setcolor
	inc al
	call setcolor
	inc al
	call setcolor

	dec cx
	jnz p6
ret ; setyellowpalette

;----------------------------------------------------------------------------
;	SETGRAYPALETTE
;
; Guess.
;
; Regs destroyed.
;
;----------------------------------------------------------------------------
setgraypalette:
	mov dx, 03c8h
	mov al, 0
	out dx, al
	mov cx, 63
	mov dx, 03c9h

	mov al, 0

palloop:
	out dx, al
	out dx, al
	out dx, al

	out dx, al
	out dx, al
	out dx, al

	out dx, al
	out dx, al
	out dx, al

	out dx, al
	out dx, al
	out dx, al

	inc al
	dec cx

	jnz palloop
ret ; setgraypalette

;palfade:
;  push ax
;	push di
;	push dx
;	push ds
;	push cx
;
;	xor ax, ax
;	xor di, di
;
;	mov ds, palseg
;	mov cx, 255
;	mov al, 1
;
;palfadeloop:
;	mov ah, byte ptr ds:[di+0]
;	mov bl, byte ptr ds:[di+1]
;	mov bh, byte ptr ds:[di+2]
;
;	sub ah, 1
;	jnc r_ok
;	xor ah, ah
;r_ok:
;	sub bl, 1
;	jnc g_ok
;	xor bl, bl
;g_ok:
;	sub bh, 1
;	jnc b_ok
;	xor bh, bh
;b_ok:
;
;	mov byte ptr ds:[di+0], ah
;	mov byte ptr ds:[di+1], bl
;	mov byte ptr ds:[di+2], bh
;
;	call setcolor
;
;	add di, 4
;	inc al
;	dec cx
;	jnz palfadeloop
;
;	pop cx
;	pop ds
;	pop dx
;	pop di
;	pop ax
;ret ; palfade



;----------------------------------------------------------------------------
;	FLIP
;
;	Flips the virtual frame buffer to the VGA frame buffer.
;
;----------------------------------------------------------------------------
flip:
	push ds
	push es
	mov ax, fbufferseg
	mov ds, ax

	mov ax, PTR_VIDEO
	mov es, ax

	xor si, si
	xor di, di
	mov ecx, 64000/4

	rep movsd

	pop es
	pop ds
ret ; flip

;----------------------------------------------------------------------------
;	CLEAR
;
; Clear the virtual frame buffer
;
;----------------------------------------------------------------------------
clear:
	push ds
	push es
	mov ax, fbufferseg
	mov es, ax

	xor si, si
	xor di, di
	mov ecx, 64000/4
	xor eax, eax

	rep stosd

	pop es
	pop ds
ret ; clear


;----------------------------------------------------------------------------
;	INIT_STARS
;
; Initializes the star array.
;
;	ax: number of stars
;
;----------------------------------------------------------------------------
init_stars:
	mov stars, ax
	mov bx, ax

	mov ax, starseg
	mov es, ax

	mov di, 32

istarloop:
	mov dx, 0
	mov cx, 319

	call random
	mov sx, ax
	fild sx
	fstp es:[di + STAR.pos.x]

	mov dx, 0
	mov cx, 180

	call random
	mov sx, ax
	fild sx
	fstp es:[di + STAR.pos.y]

	mov dx, 0
	mov cx, 100

	call random
	mov sx, ax
	fild sx
	fdiv hundred
	fsub two
	fstp es:[di + STAR.vel.x]

	mov dx, 128
	mov cx, 255

	call random
	mov es:[di + STAR.col], al

	add di, size STAR
	dec bx
	jnz istarloop

ret ; init_stars


render_stars:
	push ds

	mov ax, fbufferseg
	mov es, ax

	mov cx, stars

	mov ds, starseg

	mov di, 32

starloop:
	xor edx, edx
	fld [di + STAR.pos.x]
	fist dword ptr [edx]

	fld [di + STAR.pos.y]
	mov eax, 4
	fist dword ptr [eax]
	
	cmp dword ptr [edx], 0
	ja xmin_ok
	add dword ptr [edx], 320
xmin_ok:

	mov si, word ptr [eax]
	mov bx, si
	shl si, 6
	shl bx, 8
	add si, bx
	add si, word ptr [edx]

	mov byte ptr es:[si], 0

	mov eax, 4
	fadd [di + STAR.vel.y]
	fst [di + STAR.pos.y]
	fistp dword ptr [eax]

	fadd [di + STAR.vel.x]
	fst [di + STAR.pos.x]
	fistp dword ptr [edx]

	mov si, word ptr [eax]
	mov bx, si
	shl si, 6
	shl bx, 8
	add si, bx
	add si, word ptr [edx]

	mov al, [di + STAR.col]
	mov byte ptr es:[si], al

	add di, size STAR
	dec cx
	jnz starloop

	pop ds
ret ; render_stars

;----------------------------------------------------------------------------
;	FUSS
;
; Renders pseudorandom noise.
;
;----------------------------------------------------------------------------
;fuss_i dw 20
;fuss:
;	push es
;	mov ax, fbufferseg
;	mov es, ax
;	xor di, di
;
;	mov bx, 64000
;fussloop:
;	mov cx, fuss_i
;	mov dx, 0
;	call random
;	add byte ptr es:[di], al
;	jnc fuss_ok
;	mov byte ptr es:[di], 255
;fuss_ok:
;	inc di
;
;	dec bx
;	jnz fussloop
;
;	pop es
;ret ; fuss

motionblur:
	mov ax, fbufferseg
	mov es, ax
	mov di, 64000-320-320

mblurloop:
	mov bh, byte ptr es:[di+320]

	mov bl, byte ptr es:[di]
	shr bl, 1
	shr bh, 1
	add bl, bh

	cmp bl, 10
	jb mb_ok
	mov dx, 0
	mov cx, 10
	call random
	sub bl, al

mb_ok:
	mov byte ptr es:[di+320], bl
	dec di
	jnz mblurloop

ret ; motionblur


;----------------------------------------------------------------------------
;	KERNEL
;
;	An ISR that runs the intro and keeps it in sync.
;
;----------------------------------------------------------------------------
section dw offset intro
fpusave db 94 dup (?)
step db ?
kernel:
	pusha
	pushf
	push es
	push ds
	push gs
	cld

	mov ax, cs
	mov ds, ax

	fsave fpusave

	inc tick

	mov step, 0
	mov eax, tick
	and ax, 63
	cmp ax, 63
	jnz no_step
	mov step, 1
no_step:
	jmp section

;----------------------------------------------------------------------------

intro:
	cmp step, 1
	jnz intro_no_step
	inc lfnumber
intro_no_step:

	cmp tick, 400
	jb cycle_done

	mov section, offset walk1
	mov demoflags, FLAG_RAYCAST
;  mov spheres, 2
	call setbluepalette

;----------------------------------------------------------------------------

walk1:
	fld pallo.x
	fadd ballspeed
	fstp pallo.x

	fld camera_x
	fadd cameraspeed
	fstp camera_x

	mov bx, offset pallo
	call do_fakeg

	cmp tick, 1100
	jb cycle_done

	mov section, offset stext1
	call setredpalette
	mov demoflags, FLAG_MOTIONBLUR+FLAG_TEXT
	mov lfnumber, 0
	mov ax, offset text1
	mov textptr, ax

;----------------------------------------------------------------------------

stext1:
	cmp step, 1
	jnz stext1_no_step
	inc lfnumber
stext1_no_step:
	cmp tick, 1600
	jb cycle_done

	mov section, offset stext2
	call setredpalette
;	mov demoflags, FLAG_MOTIONBLUR+FLAG_TEXT
	mov lfnumber, 0
	mov ax, offset text2
	mov textptr, ax

;----------------------------------------------------------------------------

stext2:
	cmp step, 1
	jnz stext2_no_step
	inc lfnumber
stext2_no_step:
	cmp tick, 2000
	jb cycle_done

	mov section, offset walk2
	call setyellowpalette
	mov demoflags, FLAG_RAYCAST
;	mov no_mask, 0
	mov lfnumber, 0
;	mov eax, zero.x
	mov eax, zero.x
	mov camera_x, eax
	mov eax, pallo_left
	mov pallo.pos.x, eax
;	mov al, 3
;	mov spheres, 3
	inc spheres
	call clear

;----------------------------------------------------------------------------

walk2:
	fld pallo.x
	fadd ballspeed
	fstp pallo.x

	fld camera_x
	fadd cameraspeed
	fstp camera_x

	fld light.y
	fadd hundred
	fdiv two
	fdiv two
	fst light.y
	fstp light.z
	
	fld light.x
	fadd ambient_light
	fdiv two
	fstp light.x

	mov bx, offset pallo
	call do_fakeg
	cmp tick, 2800
	jb cycle_done

	mov section, offset stext3
	call setbluepalette
	mov demoflags, FLAG_MOTIONBLUR+FLAG_TEXT
	mov lfnumber, 0
	mov ax, offset text3
	mov textptr, ax

	mov di, offset aurinko
	mov [di + SPHERE.flags], 0
	mov [di + SPHERE.radius], 0
	mov no_mask, 1

;----------------------------------------------------------------------------

stext3:
	cmp step, 1
	jnz stext3_no_step
	inc lfnumber
stext3_no_step:
	cmp tick, 3400
	jb cycle_done

	mov section, offset walk3
	call setredpalette
	mov demoflags, FLAG_RAYCAST+FLAG_STARS+FLAG_CLEAR
	mov lfnumber, 0
	mov no_mask, 0
	mov eax, zero.x
	mov camera_x, eax
	mov pallo.pos.x, eax
	mov al, 3
	mov spheres, al
	call clear

;----------------------------------------------------------------------------

walk3:
	fld pallo.y
	fadd ballspeed
	fstp pallo.y

	fld camera_z
	fadd cameraspeed
	fstp camera_z

	fld light.y
	fadd hundred
	fdiv four
	fst light.y
	fstp light.z
	
	mov bx, offset pallo
	call do_fakeg

	cmp tick, 3900
	jb cycle_done

	mov section, offset stext4
	call setbluepalette
	mov demoflags, FLAG_MOTIONBLUR+FLAG_TEXT
	mov no_mask, 1
	mov lfnumber, 0
	mov ax, offset text4
	mov textptr, ax

;----------------------------------------------------------------------------

stext4:
	cmp step, 1
	jnz stext4_no_step
	inc lfnumber
stext4_no_step:
	cmp tick, 4400
	jb cycle_done

	mov section, offset walk4
	call setyellowpalette
	mov demoflags, FLAG_RAYCAST+FLAG_STARS+FLAG_CLEAR

	mov lfnumber, 0
	mov no_mask, 0
	mov eax, zero.x
	mov camera_x, eax
	mov camera_z, eax
	mov eax, four
	mov pallo.pos.y, eax

	mov eax, pallo_left
	mov pallo.pos.x, eax
	mov eax, shadow
	mov light.y, eax

	mov spheres, 5
	mov vuori1.flags, SPHERE_RENDER+SPHERE_REFLECT+SPHERE_SHADOW
	mov vuori2.flags, SPHERE_RENDER+SPHERE_REFLECT+SPHERE_SHADOW

	call clear

;----------------------------------------------------------------------------

walk4:
	fld pallo.x
	fadd ballspeed
	fstp pallo.x

	fld camera_x
	fadd cameraspeed
	fstp camera_x

	mov bx, offset pallo
	call do_fakeg

	cmp tick, 4900
	jb walk4_no_zoom

	fld camera_y
	fsub QUARTER
	fstp camera_y

walk4_no_zoom:

	cmp tick, 5100
	jb cycle_done

	mov section, offset stext5
	call setbluepalette
	mov demoflags, FLAG_MOTIONBLUR+FLAG_TEXT
	mov no_mask, 1
	mov lfnumber, 0
	mov ax, offset text5
	mov textptr, ax

;----------------------------------------------------------------------------

stext5:
	cmp step, 1
	jnz stext5_no_step
	inc lfnumber
stext5_no_step:
	cmp tick, 5600
	jb cycle_done

	mov section, offset stext6

	call setyellowpalette
	mov ax, offset text6
	mov textptr, ax
	mov lfnumber, 0

;----------------------------------------------------------------------------

stext6:
	cmp step, 1
	jnz stext6_no_step
	inc lfnumber
stext6_no_step:
	cmp tick, 6000
	jb cycle_done

	mov section, offset walk5
	mov demoflags, FLAG_RAYCAST+FLAG_STARS+FLAG_CLEAR

	mov lfnumber, 0
	mov no_mask, 0

	mov eax, hundred
	mov dword ptr vuori2 + SPHERE.radius, eax
	mov dword ptr vuori2 + SPHERE.x, eax
	mov byte ptr vuori2 + SPHERE.flags, SPHERE_RENDER+SPHERE_REFLECT

	mov byte ptr vuori1 + SPHERE.flags, 0
	mov camera_y, 0

	fld1
	fstp pallo.pos.z

	mov eax, pallo_left
	mov pallo.pos.x, eax

	mov eax, shadow
	mov camera_x, eax

	mov eax, shadow
	mov light.y, eax

	mov spheres, 6

;----------------------------------------------------------------------------

walk5:
	fld camera_x
	fsub quarter
	fstp camera_x

	mov bx, offset pallo
	call do_fakeg

	mov bx, offset pallo2
	call do_fakeg

	cmp tick, 6600
	jb cycle_done

	mov section, offset stext7
	call setredpalette
	mov demoflags, FLAG_MOTIONBLUR+FLAG_TEXT
	mov no_mask, 1
	mov lfnumber, 0
	mov ax, offset text7
	mov textptr, ax

;----------------------------------------------------------------------------

stext7:
	cmp step, 1
	jnz stext7_no_step
	inc lfnumber
stext7_no_step:
	cmp tick, 7200
	jb cycle_done

	mov section, offset walk7
	mov demoflags, FLAG_RAYCAST+FLAG_STARS+FLAG_CLEAR

	mov eax, pallo_left
	mov pallo.pos.x, eax
	mov pallo.pos.y, 0
	mov no_mask, 0

;	fld1
;	fst delta
;	fstp pallo.pos.z

	mov byte ptr vuori2 + SPHERE.flags, 0
	mov byte ptr pallo2 + SPHERE.flags, 0

	call setbluepalette

;----------------------------------------------------------------------------

walk7:
	fld pallo.y
	fadd ballspeed
	fstp pallo.y

	fld camera_z
	fadd cameraspeed
	fstp camera_z

	mov bx, offset pallo
	call do_fakeg

	cmp tick, 8000
	jb cycle_done

	mov section, offset stext8
	call setbluepalette
	mov demoflags, FLAG_MOTIONBLUR+FLAG_TEXT
	mov lfnumber, 0
	mov ax, offset text8
	mov textptr, ax

;----------------------------------------------------------------------------

stext8:
	cmp step, 1
	jnz stext8_no_step
	inc lfnumber
stext8_no_step:
	cmp tick, 8800
	jb cycle_done

	mov demoflags, FLAG_DONE

;----------------------------------------------------------------------------

cycle_done:

	frstor fpusave

	mov al,020h                    ; return with iret
	out 020h,al
	pop gs
	pop ds
	pop es
	popf
	popa

;intjmp:
;	jmp far 4000h:3508h
iret ; kernel

main:
	mov ax, 13h
	int INT_VIDEO

	finit

	mov demoflags, FLAG_TEXT+FLAG_MOTIONBLUR
	mov ax, offset introtext
	mov textptr, ax

;	mov tick, 4000
;	mov ax, offset walk2
;	mov section, ax

	call hooktimer

	mov bx, ds
	add ax, bx
	add ax, 1000h
	mov rbufferseg, ax
	add ax, 1000h
	mov fbufferseg, ax
	add ax, 1000h
	mov starseg, ax

	call clear
	call setyellowpalette

	mov dx, bp
	mov ax,1130h
	mov bh,6h
	int INT_VIDEO

	mov word ptr romfont, es
	mov word ptr romfont+2, bp
	mov bp, dx

	mov ax, 100
	call init_stars

mainloop:
	test demoflags, FLAG_CLEAR
	jz no_clear
	call clear

no_clear:

	test demoflags, FLAG_MOTIONBLUR
	jz no_motionblur
	call motionblur

no_motionblur:

	test demoflags, FLAG_STARS
	jz no_stars
	call render_stars

no_stars:

;	test demoflags, FLAG_FUSS
;	jz no_fuss
;	call fuss
;
;no_fuss:

	test demoflags, FLAG_RAYCAST
	jz no_raycast
	call render
	call render_rbuffer

no_raycast:

	test demoflags, FLAG_TEXT
	jz no_text

	mov si, [textptr]
	mov al, 100
	call printtext

no_text:
	call flip

	in al, 60h
	cmp al, 1
	jnz no_esc
	or demoflags, FLAG_DONE
no_esc:

	test demoflags, FLAG_DONE
	jz mainloop

	call unhooktimer

	mov ax, 3h
	int INT_VIDEO
ret ; main

END start
