;-----------------------------------------------------------------------------
;
;     TITLE: Star field
;WRITTEN BY: DRAEDEN
;      DATE: 03/15/93
;
;     NOTES: Need 386 to execute.
;
;ASSOCIATED FILES:
;
;       STARGEN.BAS =>  Basic program that generates a set of 'randomized'
;                       numbers.  Creates STARRND.DW
;
;       STARS.TXT   =>  The text file that explains starfields...
;
;       STARRND.DW  =>  File that contains a set of shuffled numbers.
;                       Used to create 'random' star field.
;
;----------------------------------------------------------------------------

    DOSSEG
    .MODEL SMALL
    .STACK 200h
    .CODE
    .386
    ASSUME CS:@CODE, DS:@CODE
;   LOCALS

;=== GLOBALS
;=== Data Includes

INCLUDE starrnd.dw      ;file that has label StarRnd numbers

;=== DATA Structures

    Star_Struc      STRUC
        X       dw  0
        Y       dw  0
        Z       dw  0
        OldDi   dw  0       ;where to erase last dot
        Color   db  0       ;BASE color. a number 0-16 is added to it
    Star_Struc      ENDS

    StarStrucSize = 9       ;number of bytes per entry

;=== DATA

ScreenWidth EQU 320
ScreenHeight EQU 200

NumRnds     EQU 400     ;number of random numbers defined

MaxZpos     EQU 4096
MinZpos     EQU 2
MaxStars    EQU 190
NumColors   EQU 5       ;number of Base colors in the Color Chart

WarpSpeed   dw  15      ;how quickly the stars move toward ya
MaxWarp     EQU 90

Xindex      dw  30      ;index into the StarRnd chart for X & Y
Yindex      dw  230     ; -note they must be different; set em the same to
                        ;see why
Cindex      dw  0       ;index into ColorChart

ColorChart  db  0,16,32,48,64,80    ;a list of base colors (-1)

Stars       Star_Struc MaxStars DUP (<>) ;where all the data is held
NumActive   dw  0       ;number of stars active

Palette     db  3 dup (0)   ;the palette.. first entrie is BG color (black)
    i = 15
    REPT    16
            db  2*i,3*i,4*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  2*i,2*i,4*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  3*i,3*i,4*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  3*i,2*i,4*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  3*i,3*i,3*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  2*i,4*i,3*i
        i=i-1
    ENDM

;=== Code Includes
;=== SUBROUTINES

    ;finds 1st available slot for a star and puts it there
MakeStar PROC NEAR
    pusha
    mov     ax,cs
    mov     es,ax
    mov     ds,ax

    cmp     [NumActive],MaxStars    ;is there room for another star?
    jae     NoEmptySpace            

    ;search for 1st available slot

    mov     si,0
TryAgain:
    cmp     word ptr [Stars.Z+si],0     ;is this slot empty?
    je      GotOne                      ;yes, go fill it

    add     si,StarStrucSize
    cmp     si,MaxStars*StarStrucSize
    jb      TryAgain
    jmp     NoEmptySpace

GotOne:         ;si points to the record for the star to fill
    mov     di,[Yindex]         ;grab index for Ypos
    add     di,di               ;multiply by 2 to make it a WORD index
    mov     ax,[StarRnd+di]     ;get the number
    shl     ax,3                ;multiply by 8- could been done in BAS file
    mov     [Stars.Y+si],ax     ;and save off the number
    
    mov     di,[Xindex]         ;grab index for Xpos
    add     di,di               ;... same as above, but for Xpos
    mov     ax,[StarRnd+di]
    shl     ax,3
    mov     [Stars.X+si],ax

    mov     [Stars.Z+si],MaxZpos    ;reset Zpos to the max
    inc     [NumActive]             ;we added a star so increase the counter

    mov     di,[Cindex]             ;grab the color index
    mov     al,[ColorChart+di]      ;grab the BaseColor for the star
    mov     [Stars.Color+si],al     ;save it in the record

    ;increase all the index pointers

    inc     [Cindex]                ;increases the color counter
    cmp     [Cindex],NumColors
    jb      OkColor
    mov     [Cindex],0
OkColor:
    inc     [Yindex]                ;increases Yindex
    cmp     [Yindex],NumRnds        ;note that for this one we
    jb      YindNotZero             ; subtract NumRnds from Yindex if we
    sub     [Yindex],NumRnds        ; go off the end of the chart
YindNotZero:
    inc     [Xindex]                ;increase Xindex
    cmp     [Xindex],NumRnds        ;have we gone through the entire chart?
    jb      XindNotZero             ;nope...

;This clever bit of code makes more use out of the chart by increasing Yindex
; one additional unit each time Xindex goes through the entire chart... the
; result is nearly NumRND^2 random non-repeating points
        
    inc     [Yindex]                ;yes, so change Yindex so that we get a
    mov     ax,[Yindex]             ;new set of random (x,y)
    cmp     ax,[Xindex]             ;does Xindex = Yindex?
    jne     NotTheSame              ;if the index were the same, you'd see 
                                    ;a graph of the line Y = X, not good...
    inc     [Yindex]                ;if they are the same, inc Yindex again
NotTheSame:
    mov     [Xindex],0              ;reset Xindex to 0
XindNotZero:                        ;all done making the star...

NoEmptySpace:
    popa
    ret
MakeStar ENDP

DisplayStars PROC NEAR
    pusha
    mov     ax,cs
    mov     ds,ax
    mov     ax,0a000h
    mov     es,ax

    mov     si,0
DispLoop:
    mov     cx,[Stars.Z+si]
    or      cx,cx                   ;if Zpos = 0 then this star is dead...
    je      Cont                    ;continue to the next one- skip this one

    mov     di,[Stars.OldDi+si]     ;grab old Di
    mov     byte ptr es:[di],0      ;erase the star
    
    cmp     cx,MinZpos
    jl      TermStar                ;if Zpos < MinZpos then kill the star

    mov     ax,[Stars.Y+si]
    movsx   dx,ah                   ;'multiply' Ypos by 256
    shl     ax,8
    
    idiv    cx                      ;and divide by Zpos
    add     ax,ScreenHeight/2       ;center it on the screen
    mov     di,ax
    cmp     di,ScreenHeight         ;see if the star is in range. 
    jae     PreTermStar             ; If not, kill it
    imul    di,ScreenWidth          ; DI = Y*ScreenWidth

    mov     ax,[Stars.X+si]
    movsx   dx,ah                   ;multiply Xpos by 256
    shl     ax,8

    idiv    cx                      ;and divide by Zpos
    add     ax,ScreenWidth/2        ;center it on the screen
    cmp     ax,ScreenWidth          ;are we inside the screen boundries?
    jae     PreTermStar
    add     di,ax                   ; DI = Y * ScreenWidth + X

    mov     [Stars.OldDi+si],di     ;save old di

    ;calculate the color below

    add     ch,cs:[Stars.Color+si]  ;i'm dividing cx (the zpos) by 256 and
                                    ; putting the result in ch and adding
                                    ; the base color to it in one instruction
    mov     es:[di],ch              ;put the dot on the screen

    mov     ax,cs:[WarpSpeed]
    sub     cs:[Stars.Z+si],ax      ;move the stars inward at WarpSpeed

Cont:
    add     si,StarStrucSize        ;point to next record
    cmp     si,MaxStars*StarStrucSize   ;are we done yet?
    jb      DispLoop
    popa
    ret

PreTermStar:
    mov     [Stars.Z+si],1  ;this is here so that the star will get erased
    jmp     short Cont      ;next time through if I just went off and killed
                            ;the star, it would leave a dot on the screen
TermStar:
    mov     [Stars.Z+si],0  ;this actually kills the star, after it has
    dec     [NumActive]     ;been erased
    jmp     short Cont

DisplayStars ENDP

;=== CODE

START:
    mov     ax,cs
    mov     ds,ax
    mov     es,ax

    mov     ax,0013h                ;set vid mode 320x200x256 graph
    int     10h
    
    mov     dx,offset Palette
    mov     ax,1012h                ; WRITE palette 
    mov     bx,0                    
    mov     cx,256                  ;write entire palette
    int     10h                     ;doesn't matter if we didnt define it all

StarLoop:
    call    MakeStar        ;make stars 2x as thick
    call    MakeStar

    mov     dx,3dah
VRT:
    in      al,dx
    test    al,8
    jnz     VRT             ;wait until Verticle Retrace starts

NoVRT:
    in      al,dx
    test    al,8
    jz      NoVRT           ;wait until Verticle Retrace Ends

    call    DisplayStars

    mov     ah,1            ;check to see if a char is ready
    int     16h
    jz      StarLoop        ;nope, continue
    
    mov     ah,0
    int     16h             ;get the character & put in AX

    cmp     al,"+"          ;compare ASCII part (al) to see what was pressed
    jne     NotPlus

    inc     [WarpSpeed]
    cmp     [WarpSpeed],MaxWarp
    jbe     StarLoop

    mov     [WarpSpeed],MaxWarp
    jmp     StarLoop

NotPlus:
    cmp     al,"-"
    jne     NotMinus

    dec     [WarpSpeed]
    cmp     [WarpSpeed],0
    jge     StarLoop

    mov     [WarpSpeed],0
    Jmp     StarLoop

NotMinus:

    mov     ax,0003h        ;set 80x25x16 char mode
    int     10h
    mov     ax,4c00h        ;return control to DOS
    int     21h
END START
