 .286
w equ word ptr
b equ byte ptr
surfclen equ 200                ;maximum length of surface defined
Pointlen equ 4*100              ;length of point array
num_ar equ 30                   ;maximum number of areas
num_cor equ 10                  ;maximum number of corners
data segment                    ;external variables from Pascal segment
  extrn vz:word                 ;total depth
  extrn rotx:Word               ;angle of rotation
  extrn roty:Word
  extrn rotz:word
  extrn worldconst:dataptr      ;array with points
  extrn surfcconst:dataptr      ;array with surface definitions
  extrn lightsrc:word           ;flag for light source shading
  extrn sf_sort:word            ;flag for surface sorting
  extrn sf_shift:word           ;flag for surface shift suppression
  extrn Texture:Byte            ;flag for textures
  extrn Fill:Byte               ;flag for fill / wireframe model

crotx dw 0                      ;x, y and z angle as offset to
croty dw 0                      ;specific sine value
crotz dw 0

rotx_x dw 0                     ;x,y,z to x-rot
rotx_y dw 0
rotx_z dw 0
roty_x dw 0                     ;to y-rot
roty_y dw 0
roty_z dw 0
rotz_x dw 0                     ;to z-rot, final
rotz_y dw 0
rotz_z dw 0

startpoly dw 0                  ;start of definition of current area

Point      dw Pointlen dup (0);receives calculated coordinates
Pointptr   dw 0                 ;pointer in Point-Array
Point3d    dw Pointlen dup (0)  ;receives completed 3D-coordinates (texture)
mean      dw num_ar*2 dup (0)   ;list of mean z-values
meanptr   dw 0                  ;pointer in Mean-Array
n           dw 0,0,0,0,0,0      ;normal vector 32 Bit
n_amnt      dw 0                ;amount of normal vector

extrn sine:dataptr

data ends

extrn drawpol:near              ;draws area as wireframe model
extrn fillpol:near              ;fills area
extrn root:near                 ;calculates root of ax

getdelta macro                  ;calculates the two surface vectors
  mov ax,poly3d[0]              ;x: original corner
  mov delta2[0],ax              ;store temporarily in delta2
  sub ax,poly3d[8]              ;obtain difference to first point
  mov delta1[0],ax              ;and delta1 finished

  mov ax,poly3d[2]              ;y: original corner
  mov delta2[2],ax              ;store temporarily in delta2
  sub ax,poly3d[10d]            ;obtain difference to first point
  mov delta1[2],ax              ;and delta1 finished

  mov ax,poly3d[4]              ;z: original corner
  mov delta2[4],ax              ;store temporarily in delta2
  sub ax,poly3d[12d]            ;obtain difference to first point
  mov delta1[4],ax              ;and delta1 finished

  mov bp,polyn                  ;select last point
  dec bp
  shl bp,3                      ;8 bytes at a time
  mov ax,poly3d[bp]             ;get x
  sub delta2[0],ax              ;obtain difference
  mov ax,poly3d[bp+2]           ;get y
  sub delta2[2],ax              ;obtain difference
  mov ax,poly3d[bp+4]           ;get z
  sub delta2[4],ax              ;obtain difference
endm

setcoord macro source,offst     ;sets calculated screencoord
.386
  mov ax,source                 ;project coordinate
  cwd
  shld dx,ax,7
  shl ax,7
  idiv cx
   add ax,offst                 ;middle of screen is 0/0/0
  mov bx,Pointptr               ;note in Point-Array
  mov Point[bx],ax
  add Pointptr,2                ;add array pointer
endm

z2cx macro tabofs               ;moves z-coordinate to cx
  mov cx,tabofs + 4
  add cx,vz                     ;add z-translation
  mov bx,meanptr                ;note in Mean-Array
  add mean[bx],cx
endm

xrot macro zcoord,qcoord        ;rotates qcoord by x, stores in zcoord
.386
  mov bp,crotx                  ;get angle
  mov bx,[qcoord]
  shl bx,3                      ;x8, to align to point entries
  mov Pointptr,bx

  sub bx,[qcoord]               ;insg. x6, to align to world entries
  sub bx,[qcoord]
  add bx,offset worldconst      ;set to world
  mov ax,[bx]                   ;get x
  mov zcoord,ax                 ;and set unchanged

  mov ax,[bx+2]                 ;get y
  imul w ds:[bp+60d]            ;*cos rotx
  shrd ax,dx,14d
  mov cx,ax                     ;store in cx
  mov ax,[bx+4]                 ;get z
  imul w ds:[bp]                ;*-sin rotx
  shrd ax,dx,14d
  sub cx,ax
  mov zcoord+2,cx               ;y value finished and set

  mov ax,[bx+2]                 ;get y
  imul w ds:[bp]                ;*sin rotx
  shrd ax,dx,14d
  mov cx,ax                     ;store in cx
  mov ax,[bx+4]                 ;get z
  imul w ds:[bp+60d]            ;*cos rotx
  shrd ax,dx,14d
  add cx,ax
  mov zcoord+4,cx
endm

yrot macro zcoord,qcoord        ;rotates qcoord by y, stores in zcoord
  mov bp,croty                  ;get angle
  mov ax,qcoord+2               ;get y
  mov zcoord+2,ax               ;and set unchanged

  mov ax,qcoord                 ;get x
  imul w ds:[bp+60d]            ;*cos roty
  shrd ax,dx,14d
  mov cx,ax                     ;store in cx
  mov ax,qcoord+4               ;get z
  imul w ds:[bp]                ;*sin roty
  shrd ax,dx,14d
  add cx,ax
  mov zcoord,cx                 ;x value finished and set

  mov ax,qcoord                 ;get x
  imul w ds:[bp]                ;*-sin roty
  shrd ax,dx,14d
  mov cx,ax                     ;store in cx
  mov ax,qcoord+4               ;get z
  imul w ds:[bp+60d]            ;*cos roty
  shrd ax,dx,14d
  sub ax,cx
  mov zcoord+4,ax
endm

zrot macro zcoord,qcoord        ;rotates qcoord by z, saves in zcoord
  mov bx,Pointptr               ;prepare entry in 3D-Point-Array

  mov bp,crotz                  ;get angle
  mov ax,qcoord+4               ;get z
  mov zcoord+4,ax               ;and set unchanged
  mov Point3d[bx+4],ax          ;also note in 3D-Array

  mov ax,qcoord                 ;get x
  imul w ds:[bp+60d]            ;*cos rotz
  shrd ax,dx,14d
  mov cx,ax                     ;store in cx
  mov ax,qcoord+2               ;get y
  imul w ds:[bp]                ;*-sin rotz
  shrd ax,dx,14d
  sub cx,ax
  mov zcoord,cx                 ;x value finished and set
  mov Point3d[bx],cx

  mov ax,qcoord                 ;get x
  imul w ds:[bp]                ;*sin rotz
  shrd ax,dx,14d
  mov cx,ax                     ;store in cx
  mov ax,qcoord+2               ;get y
  imul w ds:[bp+60d]            ;*cos rotz
  shrd ax,dx,14d
  add cx,ax
  mov zcoord+2,cx
  mov Point3d[bx+2],cx

endm

get_normal macro                ;calculates normal vector of an area
  mov ax,delta1[2]              ;a2*b3
  imul delta2[4]
  shrd ax,dx,4
  mov n[0],ax
  mov ax,delta1[4]              ;a3*b2
  imul delta2[2]
  shrd ax,dx,4
  sub n[0],ax
  mov ax,delta1[4]              ;a3*b1
  imul delta2[0]
  shrd ax,dx,4
  mov n[2],ax
  mov ax,delta1[0]              ;a1*b3
  imul delta2[4]
  shrd ax,dx,4
  sub n[2],ax
  mov ax,delta1[0]              ;a1*b2
  imul delta2[2]
  shrd ax,dx,4
  mov n[4],ax
  mov ax,delta1[2]
  imul delta2[0]
  shrd ax,dx,4
  sub n[4],ax                   ;cross product (=normal vector) finished

  mov ax,n[0]                   ;x1 ^ 2
  imul ax
  mov bx,ax
  mov cx,dx
  mov ax,n[2]                   ;+x2 ^ 2
  imul ax
  add bx,ax
  adc cx,dx
  mov ax,n[4]                   ;+x3 ^ 2
  imul ax
  add ax,bx
  adc dx,cx                     ;sum in dx:ax
  push si
  call root                     ;root in ax
  pop si
  mov n_amnt,ax                 ;amount of normal vector finished
endm

light macro                     ;determines brightness of an area
  mov ax,n[0]
  imul l[0]                     ;light vector * normal vector
  mov bx,ax                     ;form sum in cx:bx
  mov cx,dx
  mov ax,n[2]
  imul l[2]
  add bx,ax
  adc cx,dx
  mov ax,n[4]
  imul l[4]
  add ax,bx                     ;scalar product finished in dx:ax
  adc dx,cx
  idiv l_amnt                   ;divide by l_amnt

  mov bx,n_amnt                 ;and by n_amnt
  cwd
  shld dx,ax,5                  ;values from -32 bis +32
  shl ax,5d
  mov bp,startpoly              ;prepare addressing of  surface color
  idiv bx                       ;division by denominator
  inc ax
  or ax,ax
  js turned_toward              ;if cos à positive -> turned away from the light
  xor ax,ax                     ;thus, no light
turned_toward:
  sub b polycol,al              ;cos<0 -> add to primary color
endm

code segment
assume cs:code,ds:data

public drawworld

public linecount
public polycol
public polyn
public poly2d
public poly3d
linecount dw 0
polycol dw 3                    ;current surface color
polyn   dw 0                    ;number of existing corners
poly2d dw num_cor*4 dup (0)     ;corners of polygon to be drawn
poly3d dw num_cor*4 dup (0)     ;3D corners

public Txt_No
Txt_No dw 0                     ;current texture number

public delta1,delta2
delta1      dw 0,0,0            ;plane vectors
delta2      dw 0,0,0

l            dw 11d,11d,11d     ;light vector
l_amnt      dw 19d              ;amount of light vector


drawworld proc pascal           ;draws three-dimensional world
  push ds
  push es
  push bp
  lea si,surfcconst             ;surfaces are addressed by si
  mov meanptr,0                 ;start in Mean-Array with 0
  mov ax,ds:[rotx]              ;get angle,
  shl ax,1                      ;convert as memory offset
  add ax,offset sine
  mov crotx,ax                  ;and store in help variables
  mov ax,ds:[roty]              ;exactly the same for y
  shl ax,1
  add ax,offset sine
  mov croty,ax
  mov ax,ds:[rotz]              ;and z
  shl ax,1
  add ax,offset sine
  mov crotz,ax
npoly:                          ;polygon loop
  mov startpoly,si              ;store for later use
  add si,2                      ;skip color
  mov cx,[si]                   ;get number of corners
  mov linecount,cx              ;load counter
  inc cx                        ;due to closed area
  mov w polyn,cx                ;enter in Point-Array
  add si,2                      ;move to actual coordinates

nline:
  xrot rotx_x,si                ;rotate coordinates by x
  yrot roty_x,rotx_x            ;by y
  zrot rotz_x,roty_x            ;and by z
  z2cx rotz_x                   ;get z start

  setcoord rotz_x,160           ;write coordinates
  setcoord rotz_y,100

  add si,2                      ;next corner point
  dec linecount                 ;advance line counter
  je polyok                     ;all drawn -> terminate
  jmp nline                     ;otherwise next line

polyok:
  mov bx,meanptr                ;calculate mean value:
  mov ax,mean[bx]               ;get sum
  mov cx,polyn
  dec cx
  cwd
  div cx                        ;and divide by number of corners
  mov mean[bx],ax               ;write back
  mov ax,startpoly              ;write "number" of area also
  mov mean[bx+2],ax
  add meanptr,4                 ;continue
  cmp w [si+2],0                ;polygons all finished ?
  je finished
  jmp npoly

finished:
  cmp b sf_sort,0               ;sort surfaces ?
  je no_quicksort
  call quicksort pascal,0,bx    ;sort field from 0 to current position

no_quicksort:
  mov mean[bx+4],0              ;set termination
  mov ax,cs                     ;set destination segment
  mov es,ax
  xor bx,bx                     ;start with first surface
npoly_draw:
  lea di,poly2d                 ;destination:Poly-Array
  mov bp,mean[bx+2]             ;get pointer to color and points of surface
  mov ax,ds:[bp]                ;get color and set
  mov polycol,ax

  mov texture,0                 ;Assumption: no texture
  cmp ah,0ffh                   ;texture ?
  jne no_texture
  mov texture,1                 ;yes, then set
  mov b txt_no,al               ;note number

no_texture:
  mov b lightsrc,0              ;Assumption: no shading
  cmp ah,0feh                   ;shading ?
  jne no_lightsource
  mov b lightsrc,1              ;yes, then set

no_lightsource:
  add bp,2                      ;position on number
  mov cx,ds:[bp]                ;get number of corners
  mov polyn,cx                  ;write in Poly-Array
npoint:
  add bp,2
  mov si,ds:[bp]                ;get pointer to actual point
  shl si,3                      ;3 word entry !
  add si,offset Point           ;and x/y from Point-Array to Poly-Coord.

  mov ax,[si+Point3d-Point]     ;3d-get x
  mov es:[di+poly3d-poly2d],ax  ;set 3d-x
  mov ax,[si+Point3d-Point+2]   ;3d-get y
  mov es:[di+poly3d-poly2d+2],ax;set 3d-y
  mov ax,[si+Point3d-Point+4]   ;3d-get z
  mov es:[di+poly3d-poly2d+4],ax;set 3d-z

  movsw                         ;set 2D-coordinates
  movsw

  add di,4                      ;next Poly2d entry
  dec cx                        ;all corners ?
  jne npoint

  mov bp,polyn                  ;copy first corner to last
  shl bp,3                      ;position on first point
  neg bp
  mov ax,es:[di+bp]             ;and copy
  mov es:[di],ax
  mov ax,es:[di+bp+2]
  mov es:[di+2],ax

  add di,poly3d-poly2d          ;the same for 3d-coordinates
  mov ax,es:[di+bp]             ;and copy
  mov es:[di],ax
  mov ax,es:[di+bp+2]
  mov es:[di+2],ax
  mov ax,es:[di+bp+4]
  mov es:[di+4],ax

  cmp fill,1                    ;fill surface ?
  jne lines

  getdelta                      ;yes, then calculate Delta1 and 2
  cmp b lightsrc,0              ;light source ?
  jne shade
  jmp no_light

shade:                          ;yes,
  push bx
  get_normal                    ;then normal vector
  light                         ;and calculate light
  pop bx

no_light:
  inc polyn                     ;increment number of corners
  call fillpol                  ;draw surface

next:
  add bx,4                      ;locate next surface
  cmp mean[bx],0                ;last ?
  je _npoly_draw                ;no, then continue
  jmp npoly_draw

lines:
  push bx
  call drawpol                  ;draw polygon
  pop bx
  jmp next

_npoly_draw:
  pop bp                        ;finished
  pop es
  pop ds
  ret
drawworld endp


public quicksort
quicksort proc pascal down,up:word
;sorts Mean-Array according to Quicksort algorithm

local key:word
local left:word
  push bx
  mov bx,down                  ;find middle
  add bx,up
  shr bx,1
  and bx,not 3                  ;posit on blocks of 4
  mov dx,mean[bx]               ;get key
  mov key,dx
  mov  ax,down                  ;initialize right and left with base values
  mov si,ax
  mov left,ax
  mov ax,up
  mov di,ax

  mov dx,key
left_nearer:
  cmp mean[si],dx               ;greater than key -> continue searching
  jbe left_on
  add si,4                      ;posit on next one
  jmp left_nearer               ;and check it
left_on:
  cmp mean[di],dx               ;less than key -> continue searching
  jae right_on
  sub di,4                      ;posit on next one
  jmp left_on                   ;and check it
right_on:
  cmp si,di                     ;left <= right ?
  jg end_schl                   ;no -> subarea sorted
  mov eax,dword ptr mean[si]    ;exchange mean values and positions
  xchg eax,dword ptr mean[di]
  mov dword ptr mean[si],eax

  add si,4                      ;continue moving pointer
  sub di,4
end_schl:
  cmp si,di                     ;left > right, then continue
  jle left_nearer
  mov left,si                   ;store left, due to recursion
  cmp down,di                   ;down < right -> sort left subarea
  jge right_finished
  call quicksort pascal,down,di ;continue sorting recursive halves
right_finished:
  mov si,left                   ;up > left -> sort right subarea
  cmp up,si
  jle left_finished
  call quicksort pascal,si,up  ;continue sorting recursive halves
left_finished:
  pop bx
  ret
quicksort endp

code ends
end
