; ============================================
;  4Kb Intro for the Assembly '95 competition
; ============================================

; Programmed by:
;    Kim Holviala
;    Aladininkatu 7 B 19
;    49400 Hamina
;    FINLAND


; Asm '95 rules for the 4Kb intro:
;  - Under 4Kb's  (more than enough for anything....)
;  - Not more than 3 minutes
;  - Must stop with ESC
;  - No musics


; To compile a .COM file (Turbo Assembler v3.0 -> ):
;  - Make sure INTRO.ASM and VAR.MAC are present
;  - TASM /m intro
;  - TLINK /t intro

; To compile an .EXE with full debug data:
;  - TASM /m /zi /ddebug intro
;  - TLINK /v intro


IDEAL

MODEL TINY                              ; One segment is more than enough

P386N                                   ; The only way to do it!


IFDEF debug                             ; .EXE must have an stack
   
   STACK   500h

ENDIF


INCLUDE "var.mac"                       ; Include macros for stack-variables


; Variables ===============================================

VAR     codesegment,    WORD            ; Current codesegment
VAR     vgasegment,     WORD,   0A000h  ; VGA memory
VAR     virtualscr,     WORD            ; Segment of the virtual screen
VAR     firestartscr,   WORD            ; Needed for the fire-effect

VAR     temp,           WORD            ; Temporal storage
VAR     count,          BYTE            ; Counts anything
VAR     power_meter,    WORD            ; Calculates the speed of the computer
VAR     timer,          WORD,   0       ; 60 ticks/sec clock

VAR     dot_curr_char,  WORD            ; Current charptr for the dot-scroll
VAR     dot_horiz_place,BYTE,   0       ; Next vertical column to be printed

VAR     sin_start_char, WORD            ; Start charptr for the sinus-scroll
VAR     sin_curr_char,  WORD
VAR     sin_horiz_start,BYTE,   0       ; Horizontal starting-place
VAR     sin_sinptr,     BYTE            ; Pointer to the sine-table

VAR     x_rotation,     BYTE,   0       ; Rotation around the x-axis
VAR     y_rotation,     BYTE,   0
VAR     z_rotation,     BYTE,   0
VAR     distance,       DWORD,  390000h ; Distance of the 3D-object

VAR     center_x,       WORD,   420     ; Place of the 3D-object on screen
VAR     center_y,       WORD,   80

VAR     sin_x,          DWORD           ; SIN(x_rotation)
VAR     sin_y,          DWORD
VAR     sin_z,          DWORD
VAR     cos_x,          DWORD
VAR     cos_y,          DWORD
VAR     cos_z,          DWORD

VAR     cosz_cosy,      DWORD           ; cos_z * cos_y
VAR     sinz_cosy,      DWORD
VAR     sinz_cosx,      DWORD
VAR     cosz_sinx,      DWORD
VAR     cosz_cosx,      DWORD
VAR     sinz_sinx,      DWORD
VAR     cosy_sinx,      DWORD
VAR     cosy_cosx,      DWORD

VAR     matrix0,        DWORD           ; 9 slots for the 3*3 matrix
VAR     matrix1,        DWORD
VAR     matrix2,        DWORD
VAR     matrix3,        DWORD
VAR     matrix4,        DWORD
VAR     matrix5,        DWORD
VAR     matrix6,        DWORD
VAR     matrix7,        DWORD
VAR     matrix8,        DWORD
VAR     matrix9,        DWORD

VAR     new_x,          DWORD           ; Rotated x-coordinate (on screen)
VAR     new_y,          DWORD
VAR     new_z,          DWORD

VAR     fade_in_progress, BYTE          ; Variables for color-fading
VAR     fade_source,    WORD

VAR     morph_source,   WORD            ; Variables for ball-morphing

VAR     retadress,      WORD            ; Variables for polygon-drawing
VAR     x1,             WORD
VAR     y1,             WORD
VAR     x2,             WORD
VAR     y2,             WORD
VAR     x_increase,     WORD
VAR     color,          BYTE
VAR     pgon_min_y,     WORD
VAR     pgon_max_y,     WORD

VAR     worm_x1,        BYTE            ; Variables for the worm
VAR     worm_x2,        BYTE
VAR     worm_x3,        BYTE
VAR     worm_y,         BYTE
VAR     worm_lenght,    BYTE


; Code ====================================================

CODESEG
STARTUPCODE
	

; Initialize everything -----------------------------------

type_message:
	
	VAR_INITIALIZE                  ; Initialize & allocate variables

	MOV     AX,CS                   ; Make sure SREGs are correct
	MOV     DS,AX
	MOV     ES,AX

	CLD                             ; Just to be sure

	MOV     AX,CS                   ; Calculate and store segments
	MOV     codesegment,AX
	ADD     AX,1001h
	MOV     virtualscr,AX
	ADD     AX,1001h
	MOV     firestartscr,AX

	CALL    init_palettes           ; Initialize palettes
	
	MOV     fade_in_progress,1      ; Fade all colors to black
	MOV     fade_source,OFFSET black_palette

init_fadeout:
	CALL    vert_retrace            ; Wait for an retrace before
	CALL    fade_palette            ; setting new palette

	CMP     fade_in_progress,1
	JE      init_fadeout

	MOV     AX,13h                  ; Set VGA mode 13h
	INT     10h

	CALL    get_fonts               ; Get CGA & VGA fonts
	
	CALL    create_sincos_table     ; Create full sincos-table

	CALL    init_virtual            ; Initialize virtual screen
	
	CALL    init_pgtable            ; Initialize table for polygons
	
	CALL    init_starfield          ; Initialize starfield
	
	MOV     dot_curr_char,OFFSET dot_text  ; Start dot-scroll from here

	MOV     count,30                ; Wait for the screen to settle down        
	
screen_wait:
	CALL    vert_retrace

	DEC     count
	JNZ     screen_wait


; Part 1: Ball-vectors ------------------------------------

	MOV     fade_in_progress,1      ; Fade in
	MOV     fade_source,OFFSET palette

	MOV     morph_source,OFFSET ball_objects + 84

	MOV     sin_start_char,OFFSET sintext_balls  ; Sinus-scroll start
	
	CALL    handle_sinus            ; Show sinus-scroll

ball_loop:
	CALL    draw_background         ; Redraw backround (erase balls)
	
	CALL    starfield               ; Draw stars
	
	CMP     morph_source,OFFSET ball_objects + 84*5
	JB      ball_no_end_slide       ; Is the object '95'?
	
	CMP     timer,300               ; Is it time for sliding?
	JB      ball_no_end_slide
	
	SUB     center_x,2              ; Slide object to the left

	CMP     center_x,-100           ; Time for the next part?
	JL      part_2

ball_no_end_slide:
	CMP     morph_source,OFFSET ball_objects
	JB      ball_no_start_slide     ; Is the object 'circle'?
	
	CMP     center_x,154            ; Is the object in the center of the
	JB      ball_no_start_slide     ; screen?
	
	SUB     center_x,2              ; No -> Slide object to the left

ball_no_start_slide:
	CALL    morph_balls             ; Morph ball-objects
	
	CALL    calc_matrix             ; Calculate 3D-matrix

	MOV     SI,OFFSET ball_objects
	MOV     count,28
	CALL    rotate_all              ; Rotate all balls

	MOV     SI,OFFSET screen_coords
	MOV     CX,28
	CALL    sort_coords             ; Sort balls by distance
	
	CALL    draw_balls              ; Draw balls & shadows
	
	CALL    dot_scroll              ; Update dot-scroll

	CALL    vert_retrace            ; Wait for an vertical retrace
	
	CALL    flip_screen             ; Show virtual screen
	
	INC     x_rotation              ; Rotate object
	INC     y_rotation
	ADD     z_rotation,2

	MOVZX   CX,x_rotation           ; Rotate some more (break the pattern)
	BT      CX,7
	ADC     y_rotation,0

	MOV     AH,1                    ; Check for a key
	INT     16h
	JZ      ball_loop               ; No key pressed -> Keep looping

	MOV     AH,0                    ; Get the keypress from buffer
	INT     16h

	CMP     AL,27                   ; ESC exits
	JE      exit2dos

	CMP     AL,' '                  ; Space skips this part
	JNE     ball_loop


; Part 2: Filled vectors ----------------------------------

part_2:
	MOV     center_x,160            ; Slide to left from here
	
	MOV     x_rotation,215          ; Change viewpoint
	MOV     y_rotation,128

	MOV     distance,310000h        ; Move object closer
	
	MOV     sin_start_char,OFFSET sintext_fvectors  ; Sinus-scroll start
	
	CALL    handle_sinus

	MOV     timer,0                 ; Reset timer

fvectors_loop:
	INC     timer                   ; Advance timer

	CMP     timer,100               ; Morph from dot to stick?
	JB      morph_to_stick
	
	CMP     timer,600               ; Morph from stick to flag?
	JB      morph_to_flag

	CMP     timer,1200              ; From flag to map?
	JB      morph_to_map
	
	CMP     timer,1470              ; From map to dot?
	JB      morph_to_dot
	
	JMP     part_3                  ; Jump to the next part


fvectors_back_here:
	CALL    draw_background         ; Redraw backround (erase balls)
	
	CALL    starfield               ; Draw starfield
	
	CALL    calc_matrix             ; Calculate 3D-matrix

	MOV     SI,OFFSET vector_start
	MOV     count,31
	CALL    rotate_all              ; Rotate all points

	MOV     SI,OFFSET screen_coords ; Draw bottom half
	MOV     count,8
	MOV     color,15
	CALL    draw_polygon
	
	MOV     count,6                 ; Draw upper half
	MOV     color,15
	CALL    draw_polygon
	
	MOV     count,5                 ; Draw 'head'
	MOV     color,15
	CALL    draw_polygon
	
	MOV     count,4                 ; Draw 'arm'
	MOV     color,15
	CALL    draw_polygon
	
	MOV     count,4                 ; Draw part of the arrow
	MOV     color,95
	CALL    draw_polygon
	
	MOV     count,4                 ; Draw part of the arrow
	MOV     color,95
	CALL    draw_polygon
	
	
	CALL    dot_scroll              ; Update dot-scroll

	CALL    vert_retrace            ; Wait for an vertical retrace
	
	CALL    flip_screen             ; Show virtual screen
	
	INC     z_rotation              ; Rotate viewpoint

	MOV     AH,1                    ; Check for a key
	INT     16h
	JZ      fvectors_loop           ; No key pressed -> Keep looping

	MOV     AH,0                    ; Get the keypress from buffer
	INT     16h

	CMP     AL,27                   ; ESC exits
	JE      exit2dos

	CMP     AL,' '                  ; Space skips this part
	JNE     fvectors_loop


; Part 3: Wormie ------------------------------------------

part_3:
	MOV     sin_start_char,OFFSET sintext_wormie  ; Sinus-scroll start
	
	CALL    handle_sinus            ; Handle sinus-scroll

	MOV     worm_lenght,1           ; Starting lenght
	MOV     center_x,154            ; Center of the object on screen

	MOV     timer,0                 ; Reset timer

wormie_loop:
	INC     timer                   ; Advance timer

	CMP     timer,1800              ; Time to slide to the left?
	JB      wormie_maxsize
	
	SUB     center_x,2              ; Yes -> Slide wormie

	CMP     center_x,-140           ; Time for the next part?
	JL      part_4

wormie_maxsize:        
	CMP     worm_lenght,250         ; Has the worm reached it's max size?
	JE      wormie_cont             ; Yes -> Continue

	CMP     power_meter,500         ; Do we have enough CPU-power?
	JB      wormie_cont             ; No -> Don't grow

	MOV     AX,timer                ; Does the worm need to grow?
	TEST    AX,3
	JNZ     wormie_cont

	INC     worm_lenght             ; Yes

wormie_cont:
	CALL    draw_background         ; Redraw backround (erase balls)
	
	CALL    starfield               ; Draw stars
	
	CALL    draw_wormie             ; Draw worm
	
	CALL    dot_scroll              ; Update dot-scroll

	CALL    vert_retrace            ; Wait for an vertical retrace
	
	CALL    flip_screen             ; Show virtual screen
	
	MOV     AH,1                    ; Check for a key
	INT     16h
	JZ      wormie_loop             ; No key pressed -> Keep looping

	MOV     AH,0                    ; Get the keypress from buffer
	INT     16h

	CMP     AL,27                   ; ESC exits
	JE      exit2dos

	CMP     AL,' '                  ; Space skips this part
	JNE     wormie_loop


; Part 4: The End -----------------------------------------

part_4:
	MOV     fade_in_progress,1      ; Fade all colors to black
	MOV     fade_source,OFFSET black_palette

	MOV     count,100

part4_fadeout:
	CALL    dot_scroll              ; Update dot-scroll

	CALL    vert_retrace            ; Wait for an retrace before
	CALL    fade_palette            ; setting new palette

	CALL    flip_screen             ; Show virtualscreen
	
	DEC     count                   ; Loop
	JNZ     part4_fadeout
	
	CALL    handle_fire             ; Show burning text
	
	JMP     exit2dos_no_fade        ; Exit


; Handle sinus-scroll -------------------------------------

handle_sinus:
	CALL    draw_background         ; Redraw backround (erase everything)
	
	CALL    starfield               ; Draw starfield
	
	CALL    sinus_scroll            ; Update sinus-scroll
	
	CALL    dot_scroll              ; Update dot-scroll

	CALL    vert_retrace            ; Wait for an vertical retrace
	
	CALL    fade_palette            ; Fade palette (if needed)
	
	CALL    flip_screen             ; Show virtual screen
	
	MOV     SI,sin_start_char       ; Get current sinus-scroll char
	MOV     AL,[SI]
	
	CMP     AL,0                    ; Is it 0?
	JNE     _handle_sinus_cont      ; No -> Keep scrollin'
	
	INC     sin_start_char          ; Yes -> Skip over the 0
	
	RET                             ; Quit
	
_handle_sinus_cont:
	MOV     AH,1                    ; Check for a key
	INT     16h
	JZ      handle_sinus            ; No key pressed -> Keep looping

	MOV     AH,0                    ; Get the keypress from buffer
	INT     16h

	CMP     AL,27                   ; ESC exits
	JE      exit2dos

	CMP     AL,' '                  ; Space skips this part
	JNE     handle_sinus

	RET


; Cleanup and exit ----------------------------------------

exit2dos:

	MOV     fade_in_progress,1      ; Fade all colors to black
	MOV     fade_source,OFFSET black_palette

exit_fadeout:
	CALL    vert_retrace            ; Wait for an retrace before
	CALL    fade_palette            ; setting new palette

	CMP     fade_in_progress,1
	JE      exit_fadeout

exit2dos_no_fade:
	MOV     AX,3                    ; Set normal textmode
	INT     10h

	MOV     count,30                ; Wait for the screen to settle down        
	
exit_wait:
	CALL    vert_retrace

	DEC     count
	JNZ     exit_wait

	MOV     AX,1301h                ; Print exitstring
	MOV     BX,30
	MOV     CX,OFFSET end_text_end - end_text
	MOV     DX,0101h
	MOV     BP,OFFSET end_text
	INT     10h
	
	VAR_DEALLOCATE                  ; Deallocate variables

	MOV     AX,4C00h                ; Exit 2 DOS
	INT     21h


; Initalization ===========================================

; Get CGA 8*8 & VGA 8*16 fonts ----------------------------

PROC get_fonts
	
	PUSH    BP

	MOV     AX,1130h                ; Get pointer to the CGA 8*8 font
	MOV     BH,3
	INT     10h

	MOV     AX,ES                   ; ES:BP -> DS:SI = Source adress
	MOV     DS,AX
	MOV     SI,BP

	POP     BP
	
	MOV     ES,codesegment          ; ES:DI = Destination adress
	MOV     DI,OFFSET cga_font

	MOV     CX,512                  ; Copy 512 WORDs (128 characters)

	REP MOVSW                       ; Copy font

	PUSH    BP

	MOV     AX,1130h                ; Get pointer to the VGA 8*16 font
	MOV     BH,6
	INT     10h

	MOV     AX,ES                   ; ES:BP -> DS:SI = Source adress
	MOV     DS,AX
	MOV     SI,BP

	POP     BP
	
	MOV     ES,codesegment          ; ES:DI = Destination adress
	MOV     DI,OFFSET vga_font

	MOV     CX,1024                 ; Copy 128 characters

	REP MOVSW                       ; Copy font

	MOV     DS,codesegment          ; Restore regs

	RET

ENDP


; Create full sincos-table --------------------------------

PROC create_sincos_table

	MOV     SI,OFFSET partial_sincos ; Copy first quadrant of the table
	MOV     DI,OFFSET sincos_table
	MOV     CX,65

	REP MOVSW

	MOV     SI,OFFSET sincos_table + 126

_create_sct_2nd:
	MOV     AX,[SI]                 ; 2nd quadrant is the same as the
	MOV     [DI],AX                 ; first reversed

	DEC     SI
	DEC     SI
	INC     DI
	INC     DI

	CMP     SI,OFFSET sincos_table
	JA      _create_sct_2nd

_create_sct_3rd_4th:
	MOVSW                           ; 3rd & 4th quadrants are the same as
	NEG     [WORD DI-2]             ; the 1st & 2nd, only negative

	CMP     SI,OFFSET sincos_table + 256
	JB      _create_sct_3rd_4th

	RET

ENDP


; Initialize palettes -------------------------------------

PROC init_palettes

	CALL    vert_retrace            ; Must do before reading the palette
	
	MOV     AX,1017h                ; Read current palette
	MOV     BX,0
	MOV     CX,96
	MOV     DX,OFFSET vga_palette
	INT     10h

	MOV     DI,OFFSET black_palette ; Create all black palette
	XOR     AX,AX                   ; and stuff
	MOV     CX,244

	REP STOSW

	MOV     DI,OFFSET palette       ; Create slide of greys
	MOV     CX,16                   ; 16 colors
	XOR     AX,AX

_init_pal_grey:
	STOSW                           ; Store 3 bytes (RGB)
	STOSB
	
	ADD     AX,0404h                ; Advance RGB-values

	LOOP    _init_pal_grey

	MOV     SI,OFFSET colors        ; Copy fixed colors
	MOV     CX,24

	REP MOVSW

	MOV     AL,0                    ; Create slide of blues

_init_pal_blue:
	MOV     [WORD DI],0             ; Red & green are zeros
	MOV     [DI+2],AL               ; Blue

	INC     AL                      ; Next blue is little lighter
	ADD     DI,3                    ; Increase pointer

	CMP     DI,OFFSET palette+288   ; Done?
	JB      _init_pal_blue

	RET

ENDP


; Initialize palette for the fire -------------------------

PROC init_fire_palette

	XOR     AX,AX                   ; Create colors for the fire
	MOV     BL,00

	MOV     DI,OFFSET palette       ; Starting offset of the palette
	
	MOV     count,0                 ; Reset counter

_init_pal_fire:
	INC     count                   ; Increase counter
	
	MOV     [DI],AX                 ; Store RGB
	MOV     [DI+2],BL
	
	ADD     DI,3                    ; Advance pointer
	
	CMP     count,31                ; Do we have enough reds?
	JA      _init_pal_fire_yellow
	
	ADD     AL,2                    ; No -> Increase red

	JMP     _init_pal_fire          ; Loop

_init_pal_fire_yellow:
	CMP     count,62                ; Do we have enough yellows?
	JA      _init_pal_fire_white

	ADD     AH,2                    ; No -> Increase green

	JMP     _init_pal_fire          ; Loop

_init_pal_fire_white:
	CMP     count,93                ; Do we have enough whites?
	JA      _init_pal_fire_end
	
	ADD     BL,2                    ; No -> Increase blue
	
	JMP     _init_pal_fire          ; Loop

_init_pal_fire_end:
	MOV     [DWORD DI],3F3F3F3Fh    ; Store last 2 colors
	MOV     [WORD DI+4],3F3Fh
	
	RET

ENDP


; Initialize virtual screen -------------------------------
	
PROC init_virtual

	CALL    draw_background         ; Draw background
	
	MOV     ES,virtualscr           ; Draw here
	MOV     DI,58240                ; Starting offset

	XOR     AX,AX                   ; Draw 3 black lines
	MOV     CX,240

	REP STOSD

	MOV     count,8                 ; Dot-scroll has 8 rows

_init_virt_dot:        
	MOV     AX,0018h                ; One black and one green dot
	MOV     CX,160                  ; Draw one line

	REP STOSW

	XOR     AX,AX                   ; Draw one black line
	MOV     CX,160

	REP STOSW

	DEC     count                   ; Done?
	JNZ     _init_virt_dot

	MOV     ES,codesegment          ; Restore ES
	
	RET

ENDP


; Initialize table for polygon-drawing --------------------

PROC init_pgtable

	MOV     DI,OFFSET pg_table      ; Fill the table with 8000h
	MOV     CX,200
	MOV     AX,8000h

	REP STOSW

	RET

ENDP


; Initialize starfield ------------------------------------

PROC init_starfield

	MOV     DI,OFFSET starfield_data ; Stardata offset
	MOV     SI,100h                 ; Source for random numbers
	XOR     BX,BX                   ; First star's offset
	MOV     CX,100                  ; Create 200 stars

_init_stars_loop:
	XOR     AX,AX
	
	LODSB                           ; Get random number
	ADD     AX,270
	
	ADD     BX,AX                   ; Advance pointer

	MOV     AX,BX                   ; Store pointer
	STOSW

	ROR     AL,4
	STOSB                           ; Store color
	
	LOOP    _init_stars_loop

	RET

ENDP


; Color routines ==========================================

; Set palette ---------------------------------------------

PROC set_palette

	MOV     DX,3C8h                 ; Start from color 0
	XOR     AL,AL
	OUT     DX,AL

	INC     DX                      ; Output to port 3C9
	MOV     CX,288                  ; Set 288 registers (96 colors)
	MOV     SI,OFFSET vga_palette   ; Offset of the palette

	REP OUTSB                       ; Set DACs

	RET

ENDP


; Fade palette --------------------------------------------

PROC fade_palette

	CMP     fade_in_progress,0      ; Do we have to do it?
	JE      _fade_pal_end

	MOV     SI,fade_source          ; Destination palette
	MOV     DI,OFFSET vga_palette   ; Current palette
	MOV     CX,288
	CALL    morph                   ; Morph (fade) palette

	CMP     AX,0                    ; Are we through with the fading?
	JE      _fade_pal_set

	MOV     fade_in_progress,0      ; Yep

_fade_pal_set:        
	CALL    set_palette             ; Set new palette

_fade_pal_end:
	RET

ENDP


; Scroll routines =========================================

; Update dot-scroll on the bottom of the screen -----------

PROC dot_scroll

	CALL    dot_2left               ; Scroll to the left

	MOV     ES,virtualscr           ; Scroll here

	DEC     dot_horiz_place         ; Are we through with the last char?
	JNS     _dot_still_the_same

	INC     dot_curr_char           ; Yep -> Next char from the string
	MOV     dot_horiz_place,7

_dot_still_the_same:
	MOV     SI,dot_curr_char        ; Get current char
	MOVZX   BX,[SI]

	CMP     BL,0                    ; Is it 0?
	JNE     _dot_char_not_0         ; Nope -> Continue

	MOV     dot_curr_char,OFFSET dot_text ; Yes -> Reset charptr
	
_dot_char_not_0:
	SHL     BX,3                    ; Every char takes 8 bytes
	ADD     BX,OFFSET cga_font      ; BX = Ptr to the char in font

	MOV     DI,0E87Eh               ; Start drawing from here
	MOVZX   DX,dot_horiz_place      ; Draw this column from the char

_dot_loop:
	XOR     AL,AL
	BT      [BX],DX                 ; Get one bit from the font
	ADC     AL,24                   ; Calculate color
	INC     BX

	MOV     [ES:DI],AL              ; Draw

	ADD     DI,640                  ; Next row (2 rows down actually)

	CMP     DI,64000                ; Loop through all rows
	JB      _dot_loop

	MOV     ES,codesegment          ; Restore ES
	
	RET
	
ENDP


; Scroll dot-scroll to the left ---------------------------

PROC dot_2left

	MOV     AX,virtualscr           ; Adress of the video memory 
	MOV     DS,AX
	MOV     ES,AX

	MOV     SI,0E742h               ; Copy from
	MOV     DI,0E740h               ; Copy to

_dot_2left_loop:        
	MOV     CX,159                  ; One line = 159 WORDs

	REP MOVSW                       ; Scroll one line

	ADD     SI,322                  ; SI & DI 2 rows down
	ADD     DI,322
	
	CMP     DI,64000                ; All lines scrolled?
	JB      _dot_2left_loop

	MOV     AX,CS                   ; Restore DS & ES
	MOV     DS,AX
	MOV     ES,AX

	RET

ENDP


; Update sinus-scroll -------------------------------------

PROC sinus_scroll

	MOV     ES,virtualscr           ; Draw here
	
	ADD     sin_sinptr,4            ; Move sinus-effect

	DEC     sin_horiz_start         ; Scroll to the left
	JNS     _sin_same_start_char    ; Next starting char?

	INC     sin_start_char          ; Yep -> Next char from the string
	MOV     sin_horiz_start,7

_sin_same_start_char:
	MOV     AX,sin_start_char       ; Startptr to text
	MOV     sin_curr_char,AX
	
	MOV     AL,sin_sinptr           ; Pointer to sinetable
	
	XOR     BX,BX                   ; Starting x-coordinate
	MOVZX   DI,sin_horiz_start      ; Horizontal position inside char

_sin_loop:
	MOVZX   SI,AL                   ; Get SIN(angle)
	SHL     SI,1                    ; Table contains WORDs
	MOV     DX,[WORD SI+sincos_table] ; Get sine

	INC     AL                      ; Increase sine-pointer
	
	SAR     DX,9                    ; Calculate y-coordinate
	ADD     DX,80

	MOV     SI,sin_curr_char        ; SI=Pointer to char in font
	MOVZX   SI,[SI]
	SHL     SI,4
	ADD     SI,OFFSET vga_font
	
	MOV     CX,15                   ; Loop through 16 rows

_sin_column_loop:
	BT      [SI],DI                 ; Get one bit from the font
	JNC     _sin_column_nodraw      ; Bit was 0 -> Don't draw

	PUSHA

	ADD     BH,DL                   ; Address = (y * 320) + x
	SHL     DX,6
	ADD     BX,DX

	MOV     SI,OFFSET sinus_colors
	ADD     SI,CX        
	
	MOV     AL,[SI]
	MOV     AH,AL

	MOV     [WORD ES:BX],AX         ; Draw 2 points

	POPA                            ; Funny, but must be here!
	PUSHA

	SUB     DX,80                   ; Calculate y-coordinate for shadow
	SAR     DX,2
	ADD     DX,155

	ADD     BH,DL                   ; Address = (y * 320) + x
	SHL     DX,6
	ADD     BX,DX

	MOV     [WORD ES:BX],1A1Ah      ; Draw 2 points of shadow

	POPA

_sin_column_nodraw:
	INC     DX                      ; Increase y-coordinate
	INC     SI                      ; Next row in the font

	LOOP    _sin_column_loop        ; Loop through all rows

	DEC     DI                      ; Next column
	JNS     _sin_char_not_finished  ; This char drawed?

	INC     sin_curr_char           ; Yes -> Next char
	
	MOV     DI,7

_sin_char_not_finished:
	ADD     BX,2                    ; Increase x-coordinate

	CMP     BX,320                  ; All columns drawed
	JNE     _sin_loop

	MOV     ES,codesegment
	
	RET

ENDP


; Graphic routines ========================================

; Wait for the next vertical retrace ----------------------

PROC vert_retrace
	
	MOV     power_meter,0           ; Zero out our counter
	
	MOV     DX,3DAh                 ; Use port 3D4

_vert_retrace_off:
	IN      AL,DX                   ; Get vertical retrace state
	
	TEST    AL,08h
	JNZ     _vert_retrace_off       ; Loop until it's off

_vert_retrace_on:
	INC     power_meter             ; Increase counter
	
	IN      AL,DX                   ; Get vertical retrace state
	
	TEST    AL,08h
	JZ      _vert_retrace_on        ; Loop until it's on

	RET

ENDP


; Draw sprite ---------------------------------------------

; BX - Sprite number
; CX - X-coordinate
; DX - Y-coordinate

PROC putsprite

	CMP     CX,305                  ; Check x-limits
	JA      _ps_end

	MOV     ES,virtualscr           ; Draw to virtual screen
	
	INC     BX
	SHL     BX,1                    ; Start of the sprite to SI
	MOV     SI,[BX+OFFSET sprites]
	ADD     SI,OFFSET sprites

	MOVZX   AX,[SI]                 ; Get width

	ADD     CH,DL                   ; Count destination adress
	SHL     DX,6
	ADD     CX,DX
	MOV     DI,CX

	MOV     DX,320                  ; DX = 320-width
	SUB     DX,AX

	ADD     AX,DI                   ; End of the first line

	MOV     BL,[SI+1]               ; Height = Lines to draw
	ADD     SI,2

	XOR     CH,CH                   ; We need this for BYTE -> WORD

_ps_loop:
	MOV     CL,[SI]                 ; Jump over the invicible area
	ADD     DI,CX
	MOV     CL,[SI+1]               ; Number of visible points
	ADD     SI,2

	SHR     CL,1                    ; Draw using WORDs

	REP MOVSW                       ; Draw

	JNC     _ps_even                ; Still one to draw?

	MOVSB                           ; Yes -> Draw BYTE

_ps_even:
	CMP     DI,AX                   ; End of this line?
	JB      _ps_loop                ; No -> Keep drawing

	ADD     AX,320                  ; Yes -> Next line ends just below
	ADD     DI,DX                   ; DI to the start of the next line

	DEC     BL                      ; All of the lines drawed?
	JNZ     _ps_loop                ; No -> Loop

	MOV     ES,codesegment
	
_ps_end:
	RET

ENDP


; Draw all balls ------------------------------------------

PROC draw_balls

	MOV     count,28                ; Number of shadows to draw
	MOV     SI,OFFSET screen_coords ; Source offset

_draw_balls_shadows:
	MOV     CX,[SI]                 ; Get x
	MOV     BX,[SI+4]               ; Get z
	SAR     BX,11                   ; Place of the shadow
	MOV     DX,155
	SUB     DX,BX
	MOV     BX,2                    ; Sprite number

	PUSH    SI
	CALL    putsprite               ; Draw shadow
	POP     SI

	ADD     SI,6                    ; Next shadow
	
	DEC     count                   ; Done?
	JA      _draw_balls_shadows

	MOV     count,28                ; Number of balls to draw
	MOV     SI,OFFSET screen_coords ; Source offset

_draw_balls_loop:
	MOV     CX,[SI]                 ; Get x
	MOV     DX,[SI+2]               ; Get y
	MOV     BX,[SI+4]               ; Get z
	SHR     BX,15                   ; Big or small ball?
	XOR     BX,1

	PUSH    SI
	CALL    putsprite               ; Draw ball
	POP     SI
	
	ADD     SI,6                    ; Next ball
	
	DEC     count                   ; Done?
	JA      _draw_balls_loop

	RET

ENDP


; Flip virtual screen to VGA ------------------------------

PROC flip_screen

	MOV     DS,virtualscr           ; Source segment
	MOV     ES,vgasegment           ; Destination segment
	XOR     DI,DI                   ; Offsets
	XOR     SI,SI
	MOV     ECX,16000               ; Copy this many DWORDs

	REP MOVSD                       ; Copy

	MOV     DS,codesegment          ; Restore Sregs
	MOV     ES,codesegment

	RET
	
ENDP


; Redraw background ---------------------------------------

PROC draw_background

	MOV     ES,virtualscr           ; Draw eveything here
	XOR     DI,DI                   ; Starting offset
	XOR     EAX,EAX                 ; Fill with color 0 (black)
	MOV     CX,9440                 ; FIll first lines

	REP STOSD

	MOV     count,64                ; Create slide of blues
	MOV     AX,2020h                ; Starting color

_init_bg_blue:
	MOV     CX,160                  ; Draw one line

	REP STOSW

	ADD     AX,0101h                ; Next color

	DEC     count                   ; Done?
	JNZ     _init_bg_blue

	MOV     ES,codesegment          ; Restore ES
	
	RET

ENDP


; Plot one point ------------------------------------------

; BX - X-coordinate
; DX - Y-coordinate
; AL - Color
; ES - Segment to draw to

PROC plot
	
	CMP     BX,319                  ; Check x-limits
	JA      _plot_end

	CMP     DX,199                  ; Check y-limits
	JA      _plot_end
	
	ADD     BH,DL                   ; Address = (y * 320) + x
	SHL     DX,6
	ADD     BX,DX

	MOV     [ES:BX],AL              ; Draw

_plot_end:
	RET

ENDP


; Draw horizontal line ------------------------------------

; AL - color
; BX - x1
; CX - x2
; DX - y
; ES - Segment to draw to

PROC scanline

	CMP     DX,184                  ; Y-coordinate out of range?
	JA      _scanl_end              ; Yes - > Quit

	CMP     CX,BX
	JE      plot                    ; x1 = x2 -> Plot one point
	JG      _scanl_no_swap          ; x1 > x2?

	XCHG    CX,BX                   ; Yes -> Swap them

_scanl_no_swap:
	CMP     BX,0                    ; x1 < 0?
	JG      _scanl_x1_ok

	XOR     BX,BX                   ; Yes -> x1 = 0

_scanl_x1_ok:
	CMP     CX,320                  ; x2 > 320?
	JL      _scanl_x2_ok

	MOV     CX,320                  ; Yes -> x2 = 320

_scanl_x2_ok:
	CMP     BX,CX                   ; Out of the screen horizontally?
	JG      _scanl_end              ; Yes -> Quit

	SUB     CX,BX                   ; Calculate lenght
	INC     CX

	ADD     BH,DL                   ; DI=y*320+x1 (starting adress)
	SHL     DX,6
	ADD     BX,DX
	MOV     DI,BX

	MOV     AH,AL                   ; Fix color for WORD-size writes

	SHR     CX,1                    ; Draw as WORDs

	REP STOSW                       ; Draw line

	JNC     _scanl_end              ; Was the lenght even?

	STOSB                           ; No -> Draw the last pixel

_scanl_end:
	RET

ENDP


; Draw polygon --------------------------------------------

; color - Color
; Coordinates are pushed to stack: 8000h,X1,Y1,X2,Y2,...,Xn,Yn(,returnadress)
; (There _MUST_ be at least three pairs of coordinates!)

PROC polygon

; This part gets the coordinates from the stack
	
	MOV     ES,virtualscr           ; Draw to virtual screen
	
	POP     retadress               ; Get returnadress

	MOV     pgon_min_y,199          ; Initialize min/max y-coordinate
	MOV     pgon_max_y,0
	
	POP     DX                      ; Get first pair of coordinates
	POP     CX

	CALL    _pgon_check_y           ; Check y for top/bottom
	
	MOV     x1,CX                   ; Save them for the last line:
	MOV     y1,DX                   ;  (Xn, Yn) - (X1, Y1)

	POP     y2                      ; Get next pair of coords
	POP     x2

	CALL    _pgline                 ; Draw the line

_polygon_loop:
	MOV     CX,x2                   ; The ending coords of the last line
	MOV     DX,y2                   ; are the starting coords of the next

	CALL    _pgon_check_y           ; Check y for top/bottom
	
	POP     y2                      ; Get next Y
	CMP     y2,8000h                ; End of the coords?
	JE      _polygon_end            ; Yes - > Quit

	POP     x2                      ; Get next X

	CALL    _pgline                 ; Draw the line

	JMP     _polygon_loop           ; Loop

_polygon_end:
	MOV     y2,DX                   ; Restore DX after POP
	MOV     CX,x1                   ; Get saved (X1, Y1)
	MOV     DX,y1

	CALL    _pgline                 ; Draw the last line

	MOV     ES,codesegment          ; Restore ES
	
	MOV     DI,pgon_min_y           ; Clear PG_TABLE between min & max
	SHL     DI,1                    ; y-coordinate
	ADD     DI,OFFSET pg_table

	MOV     CX,pgon_max_y        
	SUB     CX,pgon_min_y
	INC     CX

	MOV     AX,8000h

	REP STOSW
	
	PUSH    retadress               ; Restore returnadress

	RET                             ; Exit


; This part draws the lines and fills the polygon
; The line is drawed between (CX, DX) and (x2, y2)

_pgline:
	PUSH    x2                      ; Save (x2,y2)
	PUSH    y2

	CMP     DX,y2                   ; Is DX = y2?
	JNE     _pgl_no_horiz

	MOV     BX,x2                   ; Yes -> Draw horizontal line
	
	POP     EAX                     ; Dump saved (x2, y2)
	MOV     AL,color                ; Get color        

	JMP     scanline                ; Draw scanline

_pgl_no_horiz:
	JL      _pgl_no_swap            ; Is DX > y2?

	XCHG    x2,CX                   ; Yes -> Swap coordinates
	XCHG    y2,DX

_pgl_no_swap:
	MOV     SI,DX                   ; Save DX (IDIV destroys DX)

	MOV     AX,x2                   ; AX = x2-x1
	SUB     AX,CX
	MOV     BX,y2                   ; BX = y2-y1
	SUB     BX,DX

	CWD                             ; Convert AX -> DX:AX for IDIV
	SHL     AX,6                    ; Lower 6 bits are decimals

	IDIV    BX                      ; AX = (x2-x1)/(y2-y1)
	
	MOV     x_increase,AX           ; Save the result

	MOV     DX,SI                   ; Restore DX

	MOV     SI,CX                   ; Starting x-coordinate
	MOV     BX,CX
	SHL     SI,6                    ; Lower 6 bits are decimals

	MOV     AL,color                ; Get drawing color

	MOV     DI,DX                   ; Calculate index to the table
	SHL     DI,1
	ADD     DI,OFFSET pg_table

	ADD     SI,20h

_pgl_loop:
	MOV     CX,[DI]                 ; Get a value from the table

	CMP     CX,8000h                ; Do we have an value in there?
	JNE     _pgl_draw_scanline      ; Yes -> Draw one line

	MOV     [DI],BX                 ; No -> Store current x to the table

	JMP     _pgl_next_line          ; We don't wanna draw now

_pgl_draw_scanline:
	PUSHA                           ; Save regs
	
	CALL    scanline                ; Draw line

	POPA                            ; Restore regs

_pgl_next_line:
	ADD     SI,x_increase           ; Increase x-coordinate
	MOV     BX,SI                   ; Move it to BX (BX=integer part of x)
	SAR     BX,6                    ; Get rid of the decimals
	
	INC     DX                      ; Increase y-coordinate by 1

	ADD     DI,2
	
	CMP     DX,y2                   ; All lines drawed?
	JLE     _pgl_loop               ; No -> Keep drawing

	POP     y2                      ; Restore (x2, y2)
	POP     x2

	RET


; This subroutine checks if given Y is the top/bottom y-coordinate

_pgon_check_y:
	CMP     DX,pgon_min_y           ; Y greater than top coordinate?
	JGE     _pgon_check_max_y       ; Yes -> Skip set

	MOV     pgon_min_y,DX           ; No -> Set new top

_pgon_check_max_y:
	CMP     DX,pgon_max_y           ; Y less than bottom coordinate?
	JLE     _pgon_check_y_end       ; Yes -> Skip set

	MOV     pgon_max_y,DX           ; Set new bottom coordinate

_pgon_check_y_end:
	RET


ENDP


; Handle polygon-drawing ----------------------------------

; count - Number of points in polygon
; SI    - Pointer to coordinates
; color - Color of the polygon

PROC draw_polygon
	
	MOV     temp,SI                 ; Save SI
	
	MOV     CL,count                ; Use CL as a counter
	
	PUSH    8000h                   ; Push coordinates to stack

_draw_polygon:
	PUSH    [WORD SI]               ; Push x
	PUSH    [WORD SI+2]             ; Push y
	
	ADD     SI,6                    ; Advance pointer

	DEC     CL                      ; Loop through all points
	JNZ     _draw_polygon

	CALL    polygon                 ; Draw polygon

	MOV     SI,temp                 ; Restore SI
	

	PUSH    8000h                   ; Push shadow's coords to stack

	MOV     color,26                ; Change color

_draw_pgon_shadow:
	PUSH    [WORD SI]               ; Push x
	
	MOV     BX,[SI+4]               ; Get z
	SAR     BX,11                   ; Place of the shadow
	MOV     DX,155
	SUB     DX,BX

	PUSH    DX                      ; Push y
	
	ADD     SI,6                    ; Advance pointer

	DEC     count                   ; Loop through all points
	JNZ     _draw_pgon_shadow

	MOV     temp,SI                 ; Save SI
	
	CALL    polygon                 ; Draw shadow

	MOV     SI,temp                 ; Restore SI
	
	RET

ENDP


; Draw starfield ------------------------------------------

PROC starfield

	MOV     SI,OFFSET starfield_data ; Source offset
	MOV     CX,100                  ; Draw 200 stars
	MOV     ES,virtualscr           ; Draw here
	
_starf_loop:        
	LODSW                           ; Get offset of an star

	MOV     BX,AX

	ADD     [BYTE SI],2             ; Change color

	MOVZX   AX,[SI]                 ; Get color
	SHR     AL,3                    ; Divide it by 8 (0-255 -> 0-31)

	INC     SI                      ; Increase source pointer

	CMP     AL,15                   ; Colors 0-15 can be drawed right away
	JBE     _starf_draw

	MOV     DL,AL                   ; Colors 16-31 need some processing
	MOV     AL,31
	SUB     AL,DL

_starf_draw:
	MOV     [BYTE ES:BX],AL         ; Draw star

	LOOP    _starf_loop             ; Loop through all stars

	MOV     ES,codesegment          ; Restore ES
	
	RET

ENDP


; Draw wormie ---------------------------------------------

PROC draw_wormie

	INC     worm_x1                 ; Wiggle worm
	ADD     worm_x2,2 
	INC     worm_x3
	
	INC     worm_y

	MOV     AL,worm_x1              ; Load worm_?? variables to registers
	MOV     AH,worm_x2
	MOV     BL,worm_x3
	MOV     BH,worm_y
	
	MOV     CL,worm_lenght          ; Init loop-counter
	MOV     count,CL

_dworm_loop:        
	MOVZX   SI,AL                   ; Modify x with first sine
	SHL     SI,1
	MOV     CX,[WORD SI+sincos_table] ; Get SIN(AL)
	
	SAR     CX,8                    ; Wiggle worm
	ADD     AL,2
	
	MOVZX   SI,AH                   ; Modify x with second sine
	SHL     SI,1
	MOV     DI,[WORD SI+sincos_table] ; Get SIN(AH)
	
	SAR     DI,9                    ; Wiggle
	ADD     CX,DI
	ADD     AH,9

	MOVZX   SI,BL                   ; Modify x with 3rd sine
	SHL     SI,1
	MOV     DI,[WORD SI+sincos_table] ; Get SIN(BL)
	
	SAR     DI,9                    ; Wiggle
	ADD     CX,DI
	ADD     BL,3

	MOVZX   SI,BH                   ; Modify y with sine
	SHL     SI,1
	MOV     DX,[WORD SI+sincos_table] ; Get SIN(BH)
	
	SAR     DX,8                    ; Wiggle
	ADD     BH,3

	ADD     CX,center_x             ; Move the worm to the center of the
	ADD     DX,center_y             ; screen
	
	PUSHA                           ; Putsprite destroys regs
	
	CALL    draw_worm_dot           ; Draw one dot
	
	POPA                            ; Restore regs

	DEC     count                   ; Loop through all dots
	JNZ     _dworm_loop

	RET

ENDP


; Draw one dot for the wormie -----------------------------

; CX - X-coordinate
; DX - Y   -"-

PROC draw_worm_dot

	MOV     x1,CX                   ; Save (x,y) for the shadow
	MOV     y1,DX
	
	MOV     BX,3                    ; Draw sprite #3 (dot)
	CALL    putsprite

	MOV     CX,x1                   ; Restore (x,y)
	MOV     DX,y1

	SHR     DX,4                    ; Calculate the place of the shadow
	ADD     DX,155
	
	MOV     BX,2                    ; Draw sprite #2 (shadow)
	CALL    putsprite

	RET

ENDP


; Draw burning text ---------------------------------------

; AL - Color of the text
; DX - Startptr to text

PROC draw_burning_text

	MOV     ES,firestartscr         ; Draw here
	
	MOV     BX,35216                ; Startoffset on screen
	MOV     DI,7                    ; Startcolumn inside font

	MOV     color,AL                ; Save original color

	MOV     CX,timer                ; Randomize
	MOV     temp,CX

_dbt_loop:
	MOV     AL,color                ; Get color
	MOV     AH,AL
	
	MOV     SI,DX                   ; SI=Pointer to char in font
	MOVZX   SI,[SI]
	
	CMP     SI,0                    ; End of the string?
	JE      _dbt_end                ; Oh Yes -> Quit
	
	SHL     SI,4
	ADD     SI,OFFSET vga_font
	
	MOV     CX,15                   ; Loop through 16 rows

_dbt_column_loop:
	BT      [SI],DI                 ; Get one bit from the font
	JNC     _dbt_column_nodraw      ; Bit was 0 -> Don't draw

	MOV     [WORD ES:BX],AX         ; Draw 2 points

_dbt_column_nodraw:
	ADD     BX,320                  ; Increase y-coordinate
	INC     SI                      ; Next row in the font

	LOOP    _dbt_column_loop        ; Loop through all rows

	DEC     DI                      ; Next column in font
	JNS     _dbt_char_not_drawed    ; This char drawed?

	INC     DX                      ; Yes -> Next char
	MOV     DI,7

	MOV     AL,color                ; Get original color
	
	MOV     SI,temp                 ; Calculate random color (+/- 8)
	INC     temp                    ; For the next character
	ADD     SI,900
	MOV     CL,[SI]
	AND     CL,00001111b
	SUB     CL,6
	ADD     color,CL        

	CMP     color,96                ; Check palette limits
	JB      _dbt_char_not_drawed
	
	MOV     color,95

_dbt_char_not_drawed:
	SUB     BX,4798                 ; Next column on screen

	JMP     _dbt_loop               ; Loop

_dbt_end:
	MOV     ES,codesegment          ; Restore ES
	
	RET
		
ENDP


; Light me up ---------------------------------------------

PROC light_me_up

	MOV     ES,virtualscr           ; Init SREGs
	MOV     DS,firestartscr

	MOV     DI,8000                 ; Starting offset

_lmu_loop:
	MOVZX   AX,[DI+320]             ; Add together 4 surrounding points
	ADD     AL,[DI+318]
	MOVZX   BX,[DI+640]
	ADD     BL,[DI+642]
	ADD     AX,BX
	
	SHR     AX,2                    ; Divide sum by 4

	DEC     AL                      ; Fade
	JNS     _lmu_draw

	MOV     AL,0

_lmu_draw:
	MOV     AH,AL                   ; Draw as WORDs

	MOV     [ES:DI],AX              ; Draw
	ADD     DI,2
	
	CMP     DI,40640                ; Done?
	JB      _lmu_loop

	MOV     AX,codesegment          ; Restore SREGs
	MOV     ES,AX
	MOV     DS,AX

	RET

ENDP


; Draw mirror for the fire --------------------------------

PROC draw_mirror

	MOV     AX,virtualscr           ; Init SREGs
	MOV     ES,AX
	MOV     DS,AX

	MOV     SI,8000                 ; Starting offsets
	MOV     DI,56000

_dmirror_loop:
	MOV     CX,160                  ; Row has 160 WORDs

_dmirror_row_loop:
	LODSW                           ; Get color

	SHR     AX,1                    ; Fade color
	AND     AX,1111111101111111b
	
	STOSW                           ; Store shadow

	LOOP    _dmirror_row_loop       ; Loop through this row

	ADD     SI,320                  ; Next row
	SUB     DI,640

	CMP     SI,40640                ; Done?
	JB      _dmirror_loop

	MOV     AX,codesegment          ; Restore SREGs
	MOV     ES,AX
	MOV     DS,AX

	RET

ENDP


; Handle burning text -------------------------------------

PROC handle_fire

	MOV     ES,virtualscr           ; Clear virtualscreen
	XOR     DI,DI
	MOV     AX,DI
	MOV     CX,64010

	REP STOSW

	MOV     ES,firestartscr         ; Clear firestartscreen
	XOR     DI,DI
	MOV     CX,64010

	REP STOSW
	
	CALL    flip_screen             ; Clear VGA memory

	CALL    init_fire_palette       ; Create palette for the fire

	MOV     fade_in_progress,1      ; Fade in
	MOV     fade_source,OFFSET palette

	MOV     timer,0                 ; Reset timer

_hfire_loop:
	INC     timer                   ; Advance timer
	
	CMP     timer,750               ; Time to quit?
	JA      _hfire_quit        

	MOV     DX,OFFSET black_palette ; NULL string offset
	
	CMP     timer,700
	JA      _hfire_draw        

	MOV     DX,OFFSET burning_textstring_2 ; 2nd string offset
	
	CMP     timer,375
	JA      _hfire_draw        

	MOV     DX,OFFSET black_palette ; NULL string offset
	
	CMP     timer,350
	JA      _hfire_draw        

	MOV     DX,OFFSET burning_textstring_1 ; 1st string offset
	
_hfire_draw:        
	ADD     count,12                ; Advance pointer to sinetable
	
	MOVZX   BX,count                ; Get SIN(count)
	SHL     BX,1
	MOV     AX,[WORD BX+sincos_table]

	SAR     AX,10                   ; Calculate color
	ADD     AX,80
	
	CALL    draw_burning_text       ; Draw text

	CALL    vert_retrace            ; Wait for retrace
	
	CALL    light_me_up             ; Create fire
       
	CALL    vert_retrace            ; Wait for retrace (again!)
	
	CALL    fade_palette            ; Fade in (if needed)
	
	CALL    draw_mirror             ; Draw mirror
	
	CALL    flip_screen             ; Show virtualscreen

	MOV     AX,virtualscr           ; Swap virtualscr & firestartscr
	XCHG    AX,firestartscr
	MOV     virtualscr,AX
	
	MOV     AH,1                    ; Check for a key
	INT     16h
	JZ      _hfire_loop             ; No key pressed -> Keep looping

	MOV     AH,0                    ; Get the keypress from buffer
	INT     16h

	MOV     timer,700               ; Quit by cheating the timer
	
	JMP     _hfire_loop             ; Loop
	
_hfire_quit:
	RET

ENDP


; 3D-routines =============================================

; Get sine & cosine for an angle --------------------------

; AL - Angle (0 - 255)

; Returns:

; ECX - SIN(angle)
; EDX - COS(angle)

PROC get_one_sincos

	MOVZX   BX,AL                   ; Get SIN(angle)

	SHL     BX,1                    ; Table contains WORDs

	MOVSX   ECX,[WORD BX+sincos_table] ; Get sine

	MOV     BX,64                   ; Get COS(angle)
	SUB     BL,AL                   ; = SIN(64 - angle)

	SHL     BX,1

	MOVSX   EDX,[WORD BX+sincos_table]

	RET

ENDP


; Get all sines & cosines needed --------------------------

PROC get_sincos
	
	MOV     AL,x_rotation           ; Get sine & cosine of x_rotation

	CALL    get_one_sincos

	MOV     sin_x,ECX
	MOV     cos_x,EDX

	MOV     AL,y_rotation           ; y_rotation

	CALL    get_one_sincos

	MOV     sin_y,ECX
	MOV     cos_y,EDX

	MOV     AL,z_rotation           ; z_rotation

	CALL    get_one_sincos

	MOV     sin_z,ECX
	MOV     cos_z,EDX

	RET

ENDP


; Calculate matrix ----------------------------------------

; x/y/z_rotation - Angles of rotation

PROC calc_matrix

	CALL    get_sincos              ; Get sines & cosines

	MOV     EAX,cos_z               ; cos_z * cos_y
	IMUL    EAX,cos_y
	SAR     EAX,14
	MOV     cosz_cosy,EAX
	
	MOV     EAX,sin_z               ; sin_z * cos_y
	IMUL    EAX,cos_y
	SAR     EAX,14
	MOV     sinz_cosy,EAX
	
	MOV     EAX,sin_z               ; sin_z * cos_x
	IMUL    EAX,cos_x
	SAR     EAX,14
	MOV     sinz_cosx,EAX
	
	MOV     EAX,cos_z               ; cos_z * sin_x
	IMUL    EAX,sin_x
	SAR     EAX,14
	MOV     cosz_sinx,EAX
	
	MOV     EAX,cos_z               ; cos_z * cos_x
	IMUL    EAX,cos_x
	SAR     EAX,14
	MOV     cosz_cosx,EAX
	
	MOV     EAX,sin_z               ; sin_z * sin_x
	IMUL    EAX,sin_x
	SAR     EAX,14
	MOV     sinz_sinx,EAX
	
	MOV     EAX,cos_y               ; cos_y * sin_x
	IMUL    EAX,sin_x
	SAR     EAX,14
	MOV     cosy_sinx,EAX
	
	MOV     EAX,cos_y               ; cos_y * cos_x
	IMUL    EAX,cos_x
	SAR     EAX,14
	MOV     cosy_cosx,EAX
	
	MOV     EAX,cosz_cosy           ; matrix0 = cos_z * cos_y
	MOV     matrix0,EAX

	MOV     EAX,sinz_cosy           ; matrix1 = -sin_z * cos_y
	NEG     EAX
	MOV     matrix1,EAX

	MOV     EAX,sin_y               ; matrix2 = sin_y
	MOV     matrix2,EAX

	MOV     EAX,cosz_sinx           ; matrix3 = sin_z * cos_x +
	IMUL    EAX,sin_y               ;           cos_z * sin_x * sin_y
	SAR     EAX,14
	ADD     EAX,sinz_cosx
	MOV     matrix3,EAX

	MOV     EAX,sinz_sinx           ; matrix4 = cos_z * cos_x -
	IMUL    EAX,sin_y               ;           sin_z * sin_x * sin_y
	SAR     EAX,14
	NEG     EAX
	ADD     EAX,cosz_cosx
	MOV     matrix4,EAX
	
	MOV     EAX,cosy_sinx           ; matrix5 = -cos_y * sin_x
	NEG     EAX
	MOV     matrix5,EAX

	MOV     EAX,cosz_cosx           ; matrix6 = sin_z * sin_x -
	IMUL    EAX,sin_y               ;           cos_z * cos_x * sin_y
	SAR     EAX,14
	NEG     EAX
	ADD     EAX,sinz_sinx
	MOV     matrix6,EAX

	MOV     EAX,sinz_cosx           ; matrix7 = cos_z * sin_x +
	IMUL    EAX,sin_y               ;           sin_z * cos_x * sin_y
	SAR     EAX,14
	ADD     EAX,cosz_sinx
	MOV     matrix7,EAX

	MOV     EAX,cosy_cosx           ; matrix8 = cos_y * cos_x
	MOV     matrix8,EAX

	RET

ENDP


; Rotate one point ----------------------------------------

; Matrix _MUST_ be calculated first using correct x/y/z_rotation values

; SI       - Pointer to 3 BYTEs (x, y, z)
; distance - Distance of the point
; DI       - Pointer to 3 WORDs (rotated x, y, z)

; Returns:

; New coordinates pointed by DI (3 WORDs)

PROC rotate_point

	MOVSX   EBX,[BYTE SI]           ; Move x to EBX
	
	MOVSX   ECX,[BYTE SI+1]         ; Move y to ECX
	
	MOVSX   EDX,[BYTE SI+2]         ; Move z to EDX

	MOV     EAX,EBX                 ; Rotate x-coordinate
	IMUL    EAX,matrix0
	MOV     new_x,EAX

	MOV     EAX,ECX
	IMUL    EAX,matrix1
	ADD     new_x,EAX

	MOV     EAX,EDX
	IMUL    EAX,matrix2
	ADD     new_x,EAX

	MOV     EAX,EBX                 ; Rotate y-coordinate
	IMUL    EAX,matrix3
	MOV     new_y,EAX

	MOV     EAX,ECX
	IMUL    EAX,matrix4
	ADD     new_y,EAX

	MOV     EAX,EDX
	IMUL    EAX,matrix5
	ADD     new_y,EAX

	IMUL    EBX,matrix6             ; Rotate z-coordinate
	MOV     new_z,EBX

	IMUL    ECX,matrix7
	ADD     new_z,ECX

	IMUL    EDX,matrix8
	ADD     new_z,EDX

	MOV     ECX,new_z               ; Calculate perspective
	ADD     ECX,distance
	SAR     ECX,8

	MOV     EDX,new_x               ; X-coordinate
	MOV     AX,DX
	SAR     EDX,16
	IDIV    CX
	ADD     AX,center_x
	STOSW                           ; Store new x
	
	ADD     ECX,3000                ; Stretch object horizontally
	
	MOV     EDX,new_y               ; Y-coordinate
	MOV     AX,DX
	SAR     EDX,16
	IDIV    CX
	ADD     AX,center_y
	STOSW                           ; Store new y

	MOV     EAX,new_z
	SAR     EAX,6
	STOSW                           ; Store new z

	RET

ENDP


; Rotate all points ---------------------------------------

; Matrix _MUST_ be calculated first using correct x/y/z_rotation values

; SI    - Pointer to coordinates
; count - Number of points to rotate

PROC rotate_all

	MOV     DI,OFFSET screen_coords ; Destination offset

_rot_all_loop:
	CALL    rotate_point            ; Rotate one point

	ADD     SI,3                    ; Next point

	DEC     count                   ; All gone?
	JA      _rot_all_loop

	RET

ENDP


; Sort coordinates by distance ----------------------------

; SI - Pointer to coordinates
; CX - Number of coordinates to sort

PROC sort_coords

	MOV     BX,SI                   ; Save starting adress

	DEC     CX                      ; Calculate ending adress
	IMUL    CX,6
	ADD     CX,SI

_sort_begin:
	MOV     AX,[SI+4]               ; Get z-coordinate
	CMP     AX,[SI+10]              ; Compare to next z-coordinate
	JGE     _sort_no_swap           ; Greater already -> no swapping

	MOV     EAX,[SI]                ; Swap x & y
	XCHG    EAX,[SI+6]
	MOV     [SI],EAX
	MOV     AX,[SI+4]               ; Swap z
	XCHG    AX,[SI+10]
	MOV     [SI+4],AX

	MOV     SI,BX                   ; Start from the beginning again
	JMP     _sort_begin             ; Loop

_sort_no_swap:
	ADD     SI,6                    ; Compare next points
	
	CMP     SI,CX                   ; Done?
	JB      _sort_begin

	RET

ENDP


; Morph routines ==========================================

; Morph any array -----------------------------------------

; SI - Offset of the original array
; DI - Offset of the morphed array
; CX - Lenght of the array in BYTEs

; Returns:

; AX - 1=Morph complete

PROC morph

	MOV     AX,1                    ; Assume morph is complete

_morph_loop:
	MOV     BL,[SI]
	CMP     BL,[DI]                 ; Which way to go?
	JG      _morph_inc              ; Increase destination
	JL      _morph_dec              ; Decrease

_morph_loop2:
	INC     SI                      ; Increase pointers
	INC     DI

	LOOP    _morph_loop             ; Done?

	RET                             ; Yep

_morph_inc:
	INC     [BYTE DI]               ; Increase destination
	MOV     AX,0                    ; Morph is not complete
	JMP     _morph_loop2

_morph_dec:
	DEC     [BYTE DI]               ; Decrease destination
	MOV     AX,0
	JMP     _morph_loop2

ENDP


; Morph ball-objects --------------------------------------

PROC morph_balls

	INC     timer                   ; Increase timer

	CMP     timer,400               ; Do we have to morph?
	JB      _morph_balls_end        ; No -> Quit
	
	MOV     SI,morph_source         ; Object to morph to
	MOV     DI,OFFSET ball_objects  ; Current object
	MOV     CX,84
	CALL    morph                   ; Morph object

	CMP     AX,0                    ; Are we through with the morphing?
	JE      _morph_balls_end        ; No -> Quit

	MOV     timer,0                 ; Yep -> Init counter
	ADD     morph_source,84         ; Next object
	
_morph_balls_end:
	RET


ENDP


; Morph from dot to stick ---------------------------------

PROC morph_to_stick

	MOV     SI,OFFSET on_my_way_to_flag

	JMP     morph_fvectors

ENDP


; Morph from stick to flag --------------------------------

PROC morph_to_flag

	MOV     SI,OFFSET flag_of_finland

	JMP     morph_fvectors

ENDP


; Morph from flag to map ----------------------------------

PROC morph_to_map

	MOV     SI,OFFSET map_of_finland

	JMP     morph_fvectors

ENDP


; Morph from map to dot -----------------------------------

PROC morph_to_dot

	MOV     SI,OFFSET vector_end

ENDP


; Morph filled vectors ------------------------------------
	
PROC morph_fvectors
	
	TEST    timer,3                 ; Slow down morphing
	JNZ     _morph_fv_end
	
	MOV     DI,OFFSET vector_start  ; Morph destination
	MOV     CX,93                   ; Lenght of data

	CALL    morph                   ; Morph object

_morph_fv_end:
	JMP     fvectors_back_here

ENDP


; Data ====================================================

DATASEG

; Partial palette

colors:
	DB      17,00,00,26,00,00,35,00,00,44,00,00
	DB      53,00,00,63,00,00,63,32,32,63,51,51
	DB      00,20,00,31,63,31,00,00,15,63,15,15
	DB      27,27,12,39,39,12,52,52,07,63,63,00


; Texts for the dot-scroll

dot_text:

;        DB      "  Welcome to ""Smooth Motion"", an 4Kb-Intro released at ASSEMBLY '95.                    "
;        DB      "Since no credits were allowed, we'd like to quote "
;        DB      "the legendary Mr. J. Morrison:                    "
;        DB      "......show me the way to the next whiskey bar, "
;        DB      "but Oh, don't ask why......                    "
;        DB      "Sorry, running out of space, must loop now......                      "

	
	DB      "  Welcome to ""Smooth Motion"", an 4Kb-Intro released at ASSEMBLY '95.                    "
	DB      "This incredible (!) intro is a PULP production......                   "
	DB      "Credits:                    "
        DB      "** Kimmy - Design, Coding, GFX **                    "
	DB      "** Mr. Pink - GFX, Ideas **                    "
	DB      "** Mr. Purple - GFX **                    "
	DB      "** Dabsa - Hardware **                    "
	DB      "Sorry, running out of space, must loop now......                    "
		 
	DB      0


; Sinus-scroll stuff

sintext_balls:
	DB      "                      WELCOME TO THE ASSEMBLY '95 4Kb-INTRO COMPETITION. "
	DB      "LET'S START WITH SOME VECTORBALLS...... ", 0

sintext_fvectors:
	DB      "                      NEXT: A DEDICATION TO KING MARA...... ", 0
	
sintext_wormie:
	DB      "                      STILL MORE TO COME ???          SURE, A COOL WORMIE...... ", 0
	DB      "                      "

sinus_colors:
	DB      31, 31, 31, 30, 30, 29, 28, 13, 60, 70, 80, 90, 95, 95, 95, 95


; Endmessages

burning_textstring_1:
	DB      7, "THAT'S", 7, "ALL", 7, "FOLKS", 7, 0

burning_textstring_2:
;        DB      7, "HOPE", 7, "YA", 7, "LIKED", 7, "IT", 7, 0

	DB      7, "PULP", 7, 7, "PRODUCTION", 7, 0


	
;        DB      1
end_text:
;        DB      "ддедееІ Smooth Motion (VOTE US!) геедедд", 10, 10, 13

	DB      "ддедееІ Smooth Motion by PULP геедедд", 10, 10, 13

end_text_end:


; First quadrant of the sincos-table

partial_sincos:
	DW           0,   402,   804,  1205,  1606,  2006,  2404,  2801
	DW        3196,  3590,  3981,  4370,  4756,  5139,  5520,  5897
	DW        6270,  6639,  7005,  7366,  7723,  8076,  8423,  8765
	DW        9102,  9434,  9760, 10080, 10394, 10702, 11003, 11297
	DW       11585, 11866, 12140, 12406, 12665, 12916, 13160, 13395
	DW       13623, 13842, 14053, 14256, 14449, 14635, 14811, 14978
	DW       15137, 15286, 15426, 15557, 15679, 15791, 15893, 15986
	DW       16069, 16143, 16207, 16261, 16305, 16340, 16364, 16379
	DW       16383


; Vectorball-objects (28 balls each)

ball_objects:

; Circle
	DB      -50, 0, 0, -42,  30, 0, -16,  48, 0,  16,  48, 0,  42,  30, 0
	DB       50, 0, 0,  42, -30, 0,  16, -48, 0, -16, -48, 0, -42, -30, 0
	
	DB      -50, 0, 0, -42,  30, 0, -16,  48, 0,  16,  48, 0,  42,  30, 0
	DB       50, 0, 0,  42, -30, 0,  16, -48, 0, -16, -48, 0, -42, -30, 0
	DB      -50, 0, 0, -42,  30, 0, -16,  48, 0,  16,  48, 0,  42,  30, 0
	DB       50, 0, 0,  42, -30, 0,  16, -48, 0


; Flat
	DB      -50, -50, 0, -25, -50, 0, 0, -50, 0, 25, -50, 0, 50, -50, 0
	DB      -50, -25, 0, -25, -25, 0, 0, -25, 0, 25, -25, 0, 50, -25, 0
	DB      -50,   0, 0, -25,   0, 0,            25,   0, 0, 50,   0, 0
	DB      -50,  25, 0, -25,  25, 0, 0,  25, 0, 25,  25, 0, 50,  25, 0
	DB      -50,  50, 0, -25,  50, 0, 0,  50, 0, 25,  50, 0, 50,  50, 0
	
	DB      -50, -50, 0, -25, -50, 0, 0, -50, 0, 25, -50, 0

; Box
	DB      -40,  40,  40, -15,  40,  40, 15,  40,  40, 40,  40,  40
	DB      -40, -40,  40, -15, -40,  40, 15, -40,  40, 40, -40,  40
	DB      -40,  40, -40, -15,  40, -40, 15,  40, -40, 40,  40, -40
	DB      -40, -40, -40, -15, -40, -40, 15, -40, -40, 40, -40, -40
	DB      -40,  40,  15, -40,  40, -15, 40,  40,  15, 40,  40, -15
	DB      -40, -40,  15, -40, -40, -15, 40, -40,  15, 40, -40, -15
	DB       15,   0,  15, -15,   0,  15, 15,   0, -15, -15,  0, -15
	
; Asm
	DB      -50,  30, 0, -40,  30, 0
	DB      -60,  20, 0, -30,  20, 0,   0,  20, 0,  10,  20, 0, 36,  20, 0, 53,  20, 0
	DB      -60,  10, 0, -30,  10, 0, -12,  10, 0,   0,   5, 0, 30,  10, 0, 45,  10, 0, 60, 10, 0
	DB      -60,   0, 0, -50,   0, 0, -40,   0, 0, -30,   0, 0, 10,  -2, 0, 30,   0, 0, 60,  0, 0
	DB      -60, -10, 0, -30, -10, 0, -10, -10, 0,   0, -10, 0, 30, -10, 0, 60, -10, 0

; 95
	DB      -40,  30, 0, -30,  30, 0,  10, 30, 0, 20, 30, 0, 30, 30, 0, 40, 30, 0
	DB      -50,  20, 0, -20,  20, 0,  10, 20, 0
	DB      -50,  10, 0, -20,  10, 0,  10, 10, 0, 20, 10, 0, 30, 10, 0
	DB      -40,   0, 0, -30,   0, 0, -20,  0, 0, 40,  0, 0
	DB      -20, -10, 0,  40, -10, 0
	DB      -50, -20, 0, -20, -20, 0,  10, -20, 0, 40, -20, 0
	DB      -40, -30, 0, -30, -30, 0,  20, -30, 0, 30, -30, 0


; Vector-objects

on_my_way_to_flag:
	DB      0,   0, 0, 0,   0, 0, 0, -25, 0, 0, -25, 0
	DB      0, -25, 0, 0, -25, 0, 0,   0, 0, 0,   0, 0

	DB      0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 25, 0, 0, 25, 0, 0, 0, 0

	DB      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

	DB      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

	DB      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

	DB      0, -25, 0, 0, 25, 0, 0, 25, 0, 0, -25, 0

flag_of_finland:
	DB       32,   0, 0,  32,   0, 0,  32, -50, 0,  32, -50, 0
	DB      -32, -50, 0, -32, -50, 0, -32,   0, 0, -32,   0, 0
	
	DB      -32,  0, 0, -32,  0, 0, -32, 50, 0
	DB       32, 50, 0,  32, 50, 0,  32,  0, 0

	DB      0, 40, 0, 0, 50, 0, 32, 50, 0, 32, 50, 0, 32, 40, 0

	DB      -32, 40, 0, -32, 50, 0, -5, 50, 0, -5, 40, 0

	DB      -33, -25, 0, -33, -10, 0, 33, -10, 0, 33, -25, 0
	
	DB       -8, -51, 0,  -8,  51, 0,  8,  51, 0,  8, -51, 0

map_of_finland:
	DB       17,   0, 0,  26, -24, 0,  20, -40, 0,  8, -52, 0
	DB      -15, -58, 0, -28, -47, 0, -25, -22, 0, -5,   3, 0

	DB      -5,  3, 0, -11, 16, 0, -12, 44, 0
	DB      10, 42, 0,  15, 30, 0,  17,  0, 0

	DB      -4, 41, 0, 2, 55, 0, 8, 60, 0, 13, 55, 0, 10, 42, 0
	
	DB      -22, 47, 0, -20, 52, 0, -9, 42, 0, -10, 35, 0

	DB       -7, -55, 0, -16, -47, 0, -7, -47, 0,  2, -47, 0
	DB      -10, -48, 0, -10, -30, 0, -4, -30, 0, -4, -48, 0

	
; Sprites (ball, smaller ball, shadow & worm-dot)

sprites:
	DB      004,000,010,000,169,000,047,001,097,001,013,011,003,007,016,017
	DB      017,017,017,017,016,003,000,001,011,016,018,018,019,019,019,019
	DB      019,017,017,016,001,000,001,011,018,019,021,021,021,021,020,020
	DB      019,018,017,001,000,000,013,018,019,021,022,023,022,021,021,020
	DB      019,019,018,017,000,013,018,019,021,023,023,023,021,021,020,020
	DB      019,018,017,000,013,018,019,021,022,023,022,021,021,020,020,019
	DB      018,017,000,013,018,019,020,021,021,021,021,020,020,019,019,018
	DB      017,000,013,018,019,019,020,020,020,020,020,019,019,018,018,017
	DB      001,011,017,019,019,019,019,019,019,018,018,018,017,001,000,001
	DB      011,016,017,017,018,018,018,018,018,017,017,016,001,000,003,007
	DB      016,017,017,017,017,017,016,003,000,012,010,003,006,016,017,017
	DB      017,017,016,003,000,001,010,016,018,018,018,018,018,018,017,017
	DB      016,001,000,001,010,018,019,021,021,021,020,019,018,018,017,001
	DB      000,000,012,016,019,021,021,022,021,021,020,019,018,017,016,000
	DB      012,018,019,021,022,022,022,021,020,019,018,018,017,000,012,018
	DB      019,021,021,022,021,021,020,019,018,018,017,000,012,016,019,019
	DB      021,021,021,020,020,019,018,017,016,001,010,017,018,019,020,020
	DB      019,019,018,018,017,001,000,001,010,016,017,017,018,018,018,018
	DB      017,017,016,001,000,003,006,016,017,017,017,017,016,003,000,011
	DB      004,002,007,026,026,026,026,026,026,026,002,000,000,011,026,026
	DB      026,026,026,026,026,026,026,026,026,000,011,026,026,026,026,026
	DB      026,026,026,026,026,026,002,007,026,026,026,026,026,026,026,002
	DB      000,013,010,004,005,023,023,023,023,023,004,000,002,009,023,023
	DB      022,022,022,022,022,022,021,002,000,001,011,023,022,022,022,027
	DB      027,027,021,021,021,020,001,000,001,011,023,022,027,027,027,027
	DB      027,027,021,021,020,001,000,000,013,023,022,022,027,027,027,027
	DB      027,027,021,020,020,019,000,013,023,022,027,027,027,027,027,027
	DB      021,021,020,019,019,001,011,022,021,021,027,027,021,021,021,020
	DB      020,019,001,000,001,011,022,021,021,021,021,021,020,020,020,019
	DB      019,001,000,002,009,021,020,020,020,020,020,019,019,019,002,000
	DB      004,005,019,019,019,019,019,004,000


; Undefined data ==========================================

UDATASEG

vga_palette:
	DB      288 DUP (?)             ; Space for palettes (96 colors each)

palette:
	DB      288 DUP (?)

black_palette:
	DB      288 DUP (?)

vector_start:
	DB      100 DUP (?)             ; First fvector-object

vector_end:
	DB      100 DUP (?)             ; Last fvector-object

cga_font:
	DB      1024 DUP (?)            ; Space for the CGA 8*8 font

vga_font:
	DB      2048 DUP (?)            ; Space for the VGA 8*14 font

sincos_table:
	DW      256 DUP (?)             ; Space for the full sincos-table

screen_coords:
	DW      100 DUP (?)             ; Space for the screen coordinates

pg_table:
	DW      400 DUP (?)             ; Table for polygon-drawing

starfield_data:
	DB      300 DUP (?)             ; Space for stars

END

   
   That's all folks...

   Elvis lives!
