; Star update routine            (C) 1993 M. Mackey
; MASM 5.0 compatible assembly code
;
; Turbo Pascal near-callable as
; Procedure Update(var star:StarType;i:integer);
;
;  where StarType=record
;                   x,y,z:integer;
;                   xz,yz:integer;
;                 end;
;
;  This procedure erases the star, recalculates its position and redraws 
;  it. Z values are constrained within 256...16012, and X, Y values are
;  constrained within -8192..8191 with wraparound. (Letting X, Y go
;  out to -32768..32767 works, but 80% of the stars are not visible at 
;  any given time, which is a waste of processor power. The view angle
;  can be expanded to compensate for this, but that results in a 'fish-
;  eye' effect which looks stupid.)
;    This code is released freeware. You may use and modify it as you see
;  fit, but you may not claim this code as your own work. Please give
;  me a copy of anything you do with this, especially any improved
;  versions.   
;
.MODEL small,pascal
.386                            ; note: uses 386 opcodes and registers!

parms   struc
        dw      2 dup (?)       ; pushed bp and return address 
  i     dw      ?               ; star number (for calling newstar proc)
  starp dd      ?               ; far pointer to star variable
parms ends


.DATA
XWidth  equ     319
YWidth  equ     199
XCenter equ     160
YCenter equ     100

EXTRN xltsin:word,xltcos:word    ;external variables & procedures
EXTRN yltsin:word,yltcos:word
EXTRN zltsin:word,zltcos:word
EXTRN speed:word
EXTRN rotx:byte,roty:byte,rotz:byte
EXTRN setpix:proc
EXTRN newstar:proc

STAR struc
  x     dw      ?
  y     dw      ?
  z     dw      ?
  xz    dw      ?       ; screen coord
  yz    dw      ?       ; screen coord
STAR ends

.CODE
        PUBLIC  Update
Update  PROC  near
        push    bp              ; Entry sequence - save old BP
        mov     bp,sp           ; point to local stack frame
        les     si,[bp+starp]   ; ES:SI points to star record

; check screen bounds
        cmp     word ptr es:[si+xz],0
        jl      DontErase
        cmp     word ptr es:[si+yz],0
        jl      DontErase
        cmp     word ptr es:[si+xz],xwidth
        jg      DontErase
        cmp     word ptr es:[si+yz],ywidth
        jg      DontErase
        push    es               ; es and si may be modified by setpix()
        push    si
        push    es:[si+xz]       ; push xz
        push    es:[si+yz]       ; push yz
        push    0F000h           ; push color (lo byte only counts)
                                 ; (I know this is weird but push 00000h
                                 ; pushed a byte only. This is kludgy but 
                                 ; at least it works!)
        call    setpix           ; erase old star position
        pop     si
        pop     es

DontErase:
        mov     ax,ds:[speed]
        sub     word ptr es:[si+z],ax       ; move all stars towards viewer

; do rotations: 16-bit arithmetic
Yrotate:
        cmp     [roty],0                    ; rotate about y axis?
        jz      XRotate                      

        movsx   ecx,word ptr es:[si+z]      ; load and sign-extend z
        movsx   edi,ds:[yltcos]             ; load and sign-extend cos value
        imul    ecx,edi                     ; ecx holds signed 32-bit cos*z
        movsx   ebx,word ptr es:[si+x]      ; load and sign-extend x
        movsx   edx,ds:[yltsin]             ; load and sign-extend sin value
        imul    ebx,edx                     ; ebx holds signed 32-bit sin*x
        sub     ecx,ebx                     ; ecx= yltcos * z -yltsin * x
        sar     ecx,15                      ;          div 32768
                                ; cx now contains signed word z value

        movsx   eax,word ptr es:[si+x]      ; load and sign-extend x
        imul    eax,edi                     ; eax holds cos*x
        movsx   ebx,word ptr es:[si+z]      ; load and sign-extend z
        imul    ebx,edx                     ; ebx holds sin*z
        add     eax,ebx                     ; eax= yltcos * x + yltsin * z
        sar     eax,15                      ;           div 32768
                                ; ax now contains signed word x value

        mov     es:[si+x],ax
        mov     es:[si+z],cx    ; finished rotation

XRotate:
        cmp     [rotx],0        ; rotate about x axis?
        jz      ZRotate

        movsx   ecx,word ptr es:[si+z]
        movsx   edi,ds:[xltcos]
        imul    ecx,edi                     ; ecx holds signed 32-bit cos*z
        movsx   ebx,word ptr es:[si+y]
        movsx   edx,ds:[xltsin]
        imul    ebx,edx                     ; ebx holds signed 32-bit sin*x
        sub     ecx,ebx                     ; ecx= xltcos * z - xltsin * y
        sar     ecx,15                      ;          div 32768
                                ; cx now contains signed word z value

        movsx   eax,word ptr es:[si+y]
        imul    eax,edi                     ; eax holds cos*y
        movsx   ebx,word ptr es:[si+z]
        imul    ebx,edx                     ; ebx holds sin*z
        add     eax,ebx                     ; eax= xltcos * y + xltsin * z
        sar     eax,15                      ;           div 32768
                                ; ax now contains signed word y value

        mov     es:[si+y],ax
        mov     es:[si+z],cx    ; finished rotation

ZRotate:
        cmp     [rotz],0        ; rotate about z axis?
        jz      Endrotations

        movsx   ecx,word ptr es:[si+y]
        movsx   edi,ds:[zltcos]
        imul    ecx,edi                     ; ecx holds signed 32-bit cos*z
        movsx   ebx,word ptr es:[si+x]
        movsx   edx,ds:[zltsin]
        imul    ebx,edx                     ; ebx holds signed 32-bit sin*x
        sub     ecx,ebx                     ; ecx= zltcos * y - zltsin * x
        sar     ecx,15                      ;          div 32768
                                ; cx now contains signed word y value

        movsx   eax,word ptr es:[si+x]
        imul    eax,edi                     ; eax holds cos*x
        movsx   ebx,word ptr es:[si+y]
        imul    ebx,edx                     ; ebx hold sin*z
        add     eax,ebx                     ;eax= zltcos * x + zltsin * y
        sar     eax,15                      ;           div 32768
                                ; ax now contains signed word x value

        mov     es:[si+x],ax
        mov     es:[si+y],cx    ; finished rotation

Endrotations:
;check bounds
        cmp     es:[si+z],256   ; z<256 gets a divide 
                                ; by zero exception later on
        jl      GetNewstar
        cmp     es:[si+z],15756
        jg      GetNewstar
        jmp     Z_OK

GetNewstar:
        push    es              ; es, di may be modified by newstar()
        push    si
        push    [bp+i]
        call    newstar
               ; problem: stars are different when you reverse direction
               ;  However, if new (random) stars are not added the 
               ;  existing stars clump together because of inaccuracy
               ;  in the 16-bit arithmetic. Change to 32-bit asap.
        pop     si
        pop     es


Z_OK:
  ; z is between 256 and 16012
        shl     es:[si+x],2
        sar     es:[si+x],2      ; get x modulo 8192 (it works! try it)
        shl     es:[si+y],2
        sar     es:[si+y],2      ; get y modulo 8192

        movsx   edx,es:[si+x]
        mov     ax,dx            ; sign-extend x to dx:ax
        shr     edx,16

        mov     cx,es:[si+z]
        shr     cx,8            ; get hi(z) in cx
        idiv    cx              ; ax = x div hi(z) (dx:ax div cx)
        add     ax,xcenter      ; xz = (x div hi(z)) + xcenter 
                                ;  (perspective projection)
        mov     es:[si+xz],ax

        movsx   edx,es:[si+y]   
        mov     ax,dx           ; sign-extend y to dx:ax
        shr     edx,16

        idiv    cx
        add     ax,ycenter
        mov     es:[si+yz],ax   ; yz = (y div hi(z)) + ycenter
                                ;   (perspective projection)

; check screen bounds
        cmp     word ptr es:[si+xz],0
        jl      Finished
        cmp     word ptr es:[si+yz],0
        jl      Finished
        cmp     word ptr es:[si+xz],xwidth
        jg      Finished
        cmp     word ptr es:[si+yz],ywidth
        jg      Finished

; draw star in grayscale based on z distance
        push    es:[si+xz]
        push    es:[si+yz]
        mov     ax,31
        mov     cx,es:[si+z]
        shr     cx,10           ; cx = z shr 10  ( ranges from 0 to 15)
        sub     ax,cx           ; ax = 31- (z shr 10)
                                ;    i.e colour between 16 and 31
                                ; (VGA colours 16-31 are a grayscale)
        push    ax
        call    setpix

Finished:
        pop     bp              
        retn    6               ; return 6 bytes of arguments

Update  endp
        end


