        .width    132
        .title    '3D transform list interpreter'

*----------------------------------------------------------------------
*                                    TIGA
*          Copyright (C) 1989-1990  Texas Instruments Incorporated.
*                            All Rights Reserved
*----------------------------------------------------------------------
* xfrmlist function
*
*   Interpret list of 3D objects to be transformed into viewing space,
*   clipped, and projected onto flat screen.  The input array is a list
*   of 3D objects to be transformed.  The output array is a list of
*   objects that have been transformed, projected onto the screen, and
*   are ready to be drawn.
*
*   The two arguments, p and q, are pointers to the start of the input
*   and output lists, respectively.  Each object specified in the input
*   list is either a polygon or a dot in 3D space.  Each object in the
*   output list is a polygon or a dot projected onto the screen.
*
*                          Input List Format
*
*   Argument p is a pointer to the input list, or transform list, which
*   is a list of polygons and dots to be transformed.  The specification
*   for each polygon or dot begins on an even 16-bit boundary in memory,
*   and the first item in each specification is a function code
*   indicating the type of graphics primitive.
*
*   The transform list is terminated by a function code of -1.
*
*   Two types of polygon may be specified in the input list:  "near" and
*   "far" polygons.  Near polygons must be preclipped; that is, they are
*   clipped to the viewport limits before being drawn.  Far polygons, on
*   the other hand, are postclipped; that is, they are clipped on a
*   line-by-line basis as they are drawn.  The division of polygons into
*   near and far types are for the purpose of computational efficiency.
*
*   Postclipping is used for small polygons.  Because preclipping
*   polygons involves some overhead, postclipping the smaller ones
*   avoids this overhead, allowing the GSP's built-in clipping hardware
*   to do the clipping automatically as each horizontal fill line is
*   drawn.
*
*   Larger polygons, however, are more efficiently dealt with by
*   preclipping, which cuts off parts of each polygon lying outside the
*   viewport which would otherwise consume considerable time during the
*   drawing phase.
*
*   Near and far polygons are specified in the tranform list by function
*   codes of 1 and 2, respectively.  The transform list specification
*   for a polygon is as follows:
*
*         16-bit function code:          1 (near) or 2 (far)
*         16-bit constant:               0
*         32-bit FIX8 x centroid coord:  xv
*         32-bit FIX8 y centroid coord:  yv
*         32-bit FIX8 z centroid coord:  zv
*         32-bit pointer to model data:  obj
*
*   Parameters xv, yv and zv are the object's centroid coordinates in
*   viewing space.  Each is represented as a 32-bit fixed-point number
*   with 8 bits of fraction.  The obj parameter is a 32-bit pointer to
*   the object model database.  The database begins with a 16-bit count
*   indicating the number of 3D points to follow.  Each point is
*   specified as a 32-bit value containing 8-bit X, Y, Z and F fields.
*   The F field specifies the logarithm to the base 2 of the scaling
*   constant for the X, Y and Z values, and is restricted to the range 0
*   to 7.  The X, Y and Z values are restricted to the range -128 to
*   +127, and after being read from the database are scaled by being
*   shifted left by F bits:  x = X << F, y = Y << F, and z = Z << F.
*
*   A "dot" is a single pixel, and is specified in the transform list by
*   a function code of 0.  The transform list format specifying a dot is
*   as follows:
*
*             16-bit function code:    0
*             16-bit dot color:        color
*             16-bit x coordinate:     xs
*             16-bit y coordinate:     ys
*
*   The second item in the specification is a 16-bit value specifying
*   the dot color.  The last two items are the x and y coordinates of
*   the dot, specified as 16-bit integers.  Note that these are the x
*   and y coordinates of the dot on the screen; in other words they have
*   been transformed and projected onto the screen before being placed
*   into the transform list.
*
*                          Output List Format
*
*   Argument q is a pointer to the output list, or display list, which
*   is a list of polygons and dots to be drawn to the screen.  The
*   specification for each polygon or dot begins on an even 16-bit
*   boundary in memory, and the first item in each specification is a
*   function code indicating the type of graphics primitive.
*
*   The display list is terminated by a function code of -1.
*
*   A polygon is specified in the display list by a function code of 1.
*   The display list specification for a polygon is as follows:
*
*             8-bit function code:     1
*             8-bit vertex count:      nverts
*             16-bit polygon color:    color
*             16-bit x coordinate:     x0
*             16-bit y coordinate:     y0
*             16-bit x coordinate:     x1
*             16-bit y coordinate:     y1
*                                      :
*             16-bit x coordinate:     x[nverts-1]
*             16-bit y coordinate:     y[nverts-1]
*
*   The second item in the specification is the number of vertices in
*   the convex polygon.  The third item is the polygon color.  The
*   remainder of the specification is an array of x and y coordinate
*   pairs representing the positions of the vertices on the screen.
*   The first xy coordinate pair specifies the position of the topmost
*   vertex in the polygon, that is, the vertex having the minimum y
*   value.  The vertices are listed in clockwise order, assuming the
*   convention that x coordinates on the screen increase from left to
*   right, and y coordinates from top to bottom. Coordinates are
*   specified as fixed point values with 2-bit fractions.
*
*   A "dot" is a single pixel, and is specified in the display list by a
*   function code of 0.  The display list format specifying a dot is as
*   follows:
*
*             8-bit function code:     0
*             8-bit constant:          0
*             16-bit polygon color:    color
*             16-bit x coordinate:     x0
*             16-bit y coordinate:     y0
*
*   The second item in the specification is a byte containing constant
*   0.  The third item is the dot color.  The last two items are the x
*   and y coordinates of the dot. Coordinates are specified as integers.
*
*   The implementation below depends on the majority of polygons being
*   "far" rather than "near".  The code that transforms "far" polygons
*   is compact enough to reside in the GSP's 256-byte instruction cache.
*   Only when a "near" polygon is encountered in the input list is this
*   code overwritten in the cache.  Note that the 250 bytes from label
*   "XFRMFAR1" to "XFRMNEAR" are preceded by a .align statement.
*
*   Prior to interpreting the input list, the 3x3 rotation matrices for
*   "near" and "far" polygons are calculated from the viewpoint
*   orientation unit vectors, u, v and w.  The first 6 elemments of each
*   matrix are stored in a a 24-word buffer allocated on the program
*   stack.  The last 3 elements are identical for both matrices, and
*   reside permanently in registers.
*-----------------------------------------------------------------------
*  Usage:  xfrmlist(p, q);
*
*  Stack parameters:
*      short *p;   /* pointer to input list to be transformed */
*      short *q;   /* pointer to output list to be drawn to screen */
*
*  Returned in A8:  32-bit pointer to end of output list
*
*  Registers altered:  A8
*-----------------------------------------------------------------------
*  Revision History:
*    01/31/89...Original version written..................Jerry Van Aken
*-----------------------------------------------------------------------
*
*
*   DECLARE GLOBAL FUNCTION NAME
*
        .globl    _xfrmlist           ;global function entry point
*
*
*   DECLARE EXTERNAL SUBROUTINES
*
        .globl    _clippgon           ;clip 3D polygon and perspect
*
*
*   DECLARE EXTERNAL VARIABLES
*
        .globl    _view               ;viewpoint parameters
        .globl    _vpclip             ;viewport clipping parameters
*
*
*   DEFINE CONSTANTS
*
        .nolist
        .copy     "fly3d.inc"         ;define flight sim. structures
        .list
; Symbolic A-file register names
TMP0    .set      A0                  ;temporary register 0
YXTOP   .set      A0                  ;xy coord's at top of polygon
TMP1    .set      A1                  ;temporary register 1
NTOP    .set      A1                  ;loop count at top of polygon
TMP2    .set      A2                  ;temporary register 2
PTOP    .set      A2                  ;pointer to top of polygon
TMP3    .set      A3                  ;temporary register 3
YX      .set      A3                  ;xy coord's at current vertex
TMP4    .set      A4                  ;temporary register 4
DIST    .set      A4                  ;viewer's distance from screen
KXX     .set      A4                  ;clip vol normalization x factor
TMP5    .set      A5                  ;temporary register 5
NVRTS   .set      A5                  ;no. of vertices in polygon
TMP6    .set      A6                  ;temporary register 6
CYCX    .set      A6                  ;viewport center xy coord's
KYY     .set      A6                  ;clip vol normalization y factor
TMP7    .set      A7                  ;temporary register 7
PTMP    .set      A7                  ;temporary pointer register
QTMP    .set      A8                  ;temporary pointer register
FZYX    .set      A8                  ;database coord's, scale factor
CMD     .set      A9                  ;function code from input list
OBJ     .set      A9                  ;pointer to object model database
Y0X0    .set      A10                 ;screen xy coord's at 1st vertex
ZVV     .set      A10                 ;centroid z coord in viewing space
Y1X1    .set      A11                 ;screen xy coord's at last vertex
YVV     .set      A11                 ;centroid y coord in viewing space
Y2X2    .set      A12                 ;screen xy coord's at 2nd vertex
XVV     .set      A12                 ;centroid x coord in viewing space
PCOLOR  .set      A12                 ;polygon color
P       .set      A13                 ;pointer to input transform list
STK     .set      A14                 ;program stack pointer
; Symbolic B-file register names
M22     .set      B0                  ;rotation matrix element m[2,2]
M21     .set      B1                  ;rotation matrix element m[2,1]
M20     .set      B2                  ;rotation matrix element m[2,0]
M12     .set      B3                  ;rotation matrix element m[1,2]
M11     .set      B4                  ;rotation matrix element m[1,1]
M10     .set      B5                  ;rotation matrix element m[1,0]
M02     .set      B6                  ;rotation matrix element m[0,2]
M01     .set      B7                  ;rotation matrix element m[0,1]
M00     .set      B8                  ;rotation matrix element m[0,0]
NPTS    .set      B9                  ;no. of points defining 3D object
COUNT   .set      B10                 ;loop counter
FMAT    .set      B11                 ;pointer to "far" rotation matrix
S       .set      B12                 ;base address of polygon xy list
T       .set      B13                 ;base address of object xy list
Q       .set      B14                 ;pointer to output display list
*
*
*   ENTRY POINT
*
_xfrmlist:

; Save registers.
        MMTM      SP,A0,A1,A2,A3,A4,A5,A6,A7,A9,A10,A11,A12,A13
        MMTM      SP,B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14
; Pop arguments p and q from program stack.
        MOVE      -*STK,P,1           ;get input pointer p
        MOVE      -*STK,QTMP,1        ;get input pointer q
        MOVE      QTMP,Q              ;save input pointer q

*-----------------------------------------------------------------------
* Save rotation matrices for "near" and "far" objects in a temporary
* buffer allocated on program stack.  The first six of the nine 32-bit
* coefficients of the "far" matrix are stored in first 12 words of
* buffer; the first six coeff's of "near" matrix are stored in last 12
* words of buffer.  The last 3 coeff's reside permanently in registers.
*-----------------------------------------------------------------------
        MOVI      _view,S             ;point to viewpoint orientation
        MOVE      S,PTMP              ;copy pointer
        MOVI      _vpclip+KX,QTMP     ;get ptr to scaling const's Kx, Ky

; Save 3D rotation matrix for "far" objects in buffer on program stack.
        MMFM      S,M00,M01,M02,M10,M11,M12,M20,M21,M22 ;get U,V,W
        SRA       14,M00              ;convert M00 = Ux to FIX16
        SRA       14,M01              ;convert M01 = Uy to FIX16
        SRA       14,M02              ;convert M02 = Uz to FIX16
        SRA       14,M10              ;convert M10 = Vx to FIX16
        SRA       14,M11              ;convert M11 = Vy to FIX16
        SRA       14,M12              ;convert M12 = Vz to FIX16
        SRA       14,M20              ;convert M20 = Wx to FIX16
        SRA       14,M21              ;convert M21 = Wy to FIX16
        SRA       14,M22              ;convert M22 = Wz to FIX16
        ADDI      6*32,STK            ;create buffer for 9 32-bit values
        MOVE      STK,FMAT            ;copy pointer
        MMTM      FMAT,M00,M01,M02,M10,M11,M12 ;store first 6 coeff's

; Scale first row of 3x3 rotation matrix for "near" objects.
        MOVE      *QTMP+,TMP2,1       ;get dist/width ratio Kx (FIX18)
        MOVE      *PTMP+,TMP0,1       ;get viewpoint component Ux (FIX30)
        MPYS      TMP2,TMP0           ;M00 = Kx * Ux
        MOVE      TMP0,*STK+,1        ;store matrix coefficient M00 (FIX16)
        MOVE      *PTMP+,TMP0,1       ;get viewpoint component Uy (FIX30)
        MPYS      TMP2,TMP0           ;M01 = Kx * Uy
        MOVE      TMP0,*STK+,1        ;store matrix coefficient M01 (FIX16)
        MOVE      *PTMP+,TMP0,1       ;get viewpoint component Uz (FIX30)
        MPYS      TMP2,TMP0           ;M02 = Kx * Uz
        MOVE      TMP0,*STK+,1        ;store matrix coefficient M02 (FIX16)
; Scale second row of 3x3 rotation matrix for "near" objects.
;---    MOVI      _vpclip+KY,Q        ;load pointer to Ky
        MOVE      *QTMP+,TMP2,1       ;get dist/height ratio Ky (FIX18)
        MOVE      *PTMP+,TMP0,1       ;get viewpoint component Vx (FIX30)
        MPYS      TMP2,TMP0           ;M10 = Ky * Vx
        MOVE      TMP0,*STK+,1        ;store matrix coefficient M10 (FIX16)
        MOVE      *PTMP+,TMP0,1       ;get viewpoint component Vy (FIX30)
        MPYS      TMP2,TMP0           ;M11 = Ky * Vy
        MOVE      TMP0,*STK+,1        ;store matrix coefficient M11 (FIX16)
        MOVE      *PTMP+,TMP0,1       ;get viewpoint component Vz (FIX30)
        MPYS      TMP2,TMP0           ;M12 = Ky * Vz
        MOVE      TMP0,*STK+,1        ;store matrix coefficient M12 (FIX16)


*-----------------------------------------------------------------------
; Interpret first function code from input list.
*-----------------------------------------------------------------------
FIRSTCMD:
        SETF      16,1,0              ;
        MOVE      *P+,CMD,0           ;input next 16-bit function code
        JRZ       PUSHDOT             ;if f.c. = 0, single dot
        DEC       CMD                 ;
        JRGT      XFRMFAR             ;if f.c. = 2, "far" polygon
        JRZ       XFRMNEAR            ;if f.c. = 1, "near" polygon
        JR        EXIT                ;if f.c. = -1, exit interpreter


*-----------------------------------------------------------------------
* Simply copy description of dot from input list to output list.
*-----------------------------------------------------------------------
PUSHDOT:

        SUBK      16,P                ;back up to dot function code
        MOVE      Q,QTMP              ;copy output list pointer
        MOVE      *P+,*QTMP+,1        ;copy dot f.c. and color
        MOVE      *P+,*QTMP+,1        ;copy dot x and y coordinates
        MOVE      QTMP,Q              ;update output list pointer
        JR        FIRSTCMD            ;more objects in input list?


*-----------------------------------------------------------------------
*             Transform "Far" (or Distant) Graphics Object
*-----------------------------------------------------------------------
XFRMFAR:

        MOVE      @_vpclip+D,DIST,0   ;get d = viewer's dist. from screen
        SLL       2,DIST              ;convert d to FIX2 format
        MOVE      @_vpclip+CX,CYCX,1  ;get viewport center coord's Cx, Cy
        ADDXY     CYCX,CYCX           ;
        ADDXY     CYCX,CYCX           ;convert Cy and Cx to FIX2 format
; Get coefficients Mij of rotation matrix.
        MOVE      FMAT,T              ;copy ptr to "far" rot'n matrix
        MMFM      T,M00,M01,M02,M10,M11,M12 ;get first 6 coefficients


        .align                        ;begin cache-resident 256 bytes

; Jump here if DIST, CYCX and Mij coefficients in registers are still
; valid.  Read FACTOR and xyz centroid coordinates in viewing space.
XFRMFAR1:
        ADDK      16,P                ;skip over constant 0
        MMFM      P,XVV,YVV,ZVV,OBJ   ;get xv, yv, zv and pointer obj
; Transform 3D points expressed as displacements from object centroid.
; Displacement coordinates are restricted to 8 significant bits to
; permit faster multiplies (field size 1 = 8).
        MOVE      *OBJ+,TMP0,0        ;get no. of points to be transformed
        MOVE      TMP0,NPTS           ;NPTS = no. of points in object
        MOVE      STK,T               ;copy pointer to perspected xy's
        SETF      32,0,0              ;set up for 32-bit memory moves
FARLOOP:
        MOVE      *OBJ+,FZYX,0        ;get X, Y, Z and F (X in 8 LSBs)

        SETF      8,1,1               ;set up for 8-bit multiplies
        MOVE      M00,TMP1            ;get rotation matrix coeff. M00 (FIX16)
        MPYS      FZYX,TMP1           ;x * M00 (FIX16)

        MOVE      M10,TMP3            ;get rotation matrix coeff. M10 (FIX16)
        MPYS      FZYX,TMP3           ;x * M10 (FIX16)

        MOVE      M20,TMP5            ;get rotation matrix coeff. M20 (FIX16)
        MPYS      FZYX,TMP5           ;x * M20 (FIX16)

        SRL       8,FZYX              ;shift Y into 8 LSBs of multiplier
        MOVE      M01,TMP7            ;get rotation matrix coeff. M01 (FIX16)
        MPYS      FZYX,TMP7           ;y * M01 (FIX16)
        ADD       TMP7,TMP1           ;x * M00 + y * M01

        MOVE      M11,TMP7            ;get rotation matrix coeff. M11 (FIX16)
        MPYS      FZYX,TMP7           ;y * M11 (FIX16)
        ADD       TMP7,TMP3           ;x * M10 + y * M11

        MOVE      M21,TMP7            ;get rotation matrix coeff. M21 (FIX16)
        MPYS      FZYX,TMP7           ;y * M21 (FIX16)
        ADD       TMP7,TMP5           ;x * M20 + y * M21

        SRL       8,FZYX              ;shift Z into 8 LSBs of multiplier
        MOVE      M02,TMP7            ;get rotation matrix coeff. M02 (FIX16)
        MPYS      FZYX,TMP7           ;z * M02 (FIX16)
        ADD       TMP7,TMP1           ;x * M00 + y * M01 + z * M02 (FIX16)

        MOVE      M12,TMP7            ;get rotation matrix coeff. M12 (FIX16)
        MPYS      FZYX,TMP7           ;z * M12 (FIX16)
        ADD       TMP7,TMP3           ;x * M10 + y * M11 + z * M12 (FIX16)

        MOVE      M22,TMP7            ;get rotation matrix coeff. M22 (FIX16)
        MPYS      FZYX,TMP7           ;z * M22 (FIX16)
        ADD       TMP7,TMP5           ;x * M20 + y * M21 + z * M22 (FIX16)

        SRL       8,FZYX              ;shift log2(factor) into 8 LSBs
        SUBK      8,FZYX              ;combine with FIX8 scaling factor
        SRA       FZYX,TMP1           ;convert x*M00+y*M01+z*M02 to FIX8
        ADD       XVV,TMP1            ;x' = xv+x*M00+y*M01+z*M02 (FIX8)

        SRA       FZYX,TMP3           ;convert x*M10+y*M11+z*M12 to FIX8
        ADD       YVV,TMP3            ;y' = yv+x*M10+y*M11+z*M12 (FIX8)

        SRA       FZYX,TMP5           ;convert x*M20+y*M21+z*M22 to FIX8
        ADD       ZVV,TMP5            ;z' = zv+x*M20+y*M21+z*M22 (FIX8)

        SETF      16,1,1              ;set up for 16-bit multiplies
        MOVE      TMP1,TMP0           ;copy x'
        MPYS      DIST,TMP0           ;d * x'
        DIVS      TMP5,TMP0           ;(d * x') / z' (FIX2 result)

        MOVE      TMP3,TMP2           ;copy y'
        MPYS      DIST,TMP2           ;d * y'
        DIVS      TMP5,TMP2           ;(d * y') / z' (FIX2 result)

        SLL       16,TMP2             ;
        MOVY      TMP2,TMP0           ;concatenate perspected x and y
        ADDXY     CYCX,TMP0           ;x"= d*x'/z'+Cx, y"= d*y'/z'+Cy
        MOVE      TMP0,*STK+,0        ;push integer x' and y' (FIX2)

        DSJ       NPTS,FARLOOP        ;more points to transform scale?

*------------------------------------------------------------------------------
*  All points defining the object have been transformed and perspective
*  projected onto screen.  Next, the description of each of the
*  individual polygons in the object will be copied to the display list.
*------------------------------------------------------------------------------
;---    SETF      32,0,0              ;set up for 32-bit memory moves
;---    SETF      16,1,1              ;set up for 16-bit memory moves
PUSH2D:
        MOVE      Q,QTMP              ;copy output list pointer
        MOVE      *OBJ+,NVRTS,1       ;get no. of vertices in polygon
        JRZ       DONE2D              ;jump if end of data encountered
        SLL       8,NVRTS             ;NVRTS in 8 MSBs of word
        INC       NVRTS               ;DRAWCODE=1 (polygon) in 8 LSBs
        MOVE      NVRTS,*QTMP+,1      ;copy DRAWCODE and NVRTS to output list
        SRL       8,NVRTS             ;restore vertex count
        MOVE      *OBJ+,*QTMP+,1      ;copy COLOR code to output list
; Extract ordered list of vertices from list of transformed coordinates.
; Search xy coordinate list for topmost vertex (minimum y) in polygon.
        MOVI      03FFF0000h,YXTOP    ;initialize ymin = max y value
        MOVE      STK,S               ;save base address of xy coord's
        MOVE      T,PTMP              ;get base address of object xy's
        MOVE      NVRTS,COUNT         ;initialize loop counter
TOP2D:
        MOVE      *OBJ+,YX,1          ;get index of next vertex in face
        SLL       5,YX                ;convert to 32-bit array offset
        ADD       PTMP,YX             ;add base address of xy coord's
        MOVE      *YX,YX,0            ;get x and y coordinates at vertex
        MOVE      YX,*STK+,0          ;push temporarily onto stack
        CMPXY     YX,YXTOP            ;is y < ymin ?
        JRYZ      SKIP2D              ;jump if y == ymin
        MOVE      COUNT,NTOP          ;make copy of current loop count
        JRYN      SKIP2D              ;jump if y > ymin
        MOVE      YX,YXTOP            ;set ymin = y
        MOVE      STK,PTOP            ;copy pointer (autoincremented)
SKIP2D:
        DSJ       COUNT,TOP2D         ;loop if more vertices
; Coordinates of topmost vertex in polygon have been found.
        CMP       NVRTS,NTOP          ;are all vertices at same y?
        JREQ      TRIV2D              ;jump to trivial reject flat face
; Perform backface test.  If the 2 edges surrounding the first vertex
; are turned in the clockwise direction, the polygon is front facing.
        MOVE      YX,Y1X1             ;get y1:x1 (last vertex in list)
        MOVE      S,PTMP              ;point to first vertex in list
        MOVE      *PTMP+,Y0X0,0       ;get y0:x0 (first vertex in list)
        MOVE      *PTMP+,Y2X2,0       ;get y2:x2 (2nd vertex in list)
        SUBXY     Y1X1,Y0X0           ;calculate y0-y1:x0-x1
        MOVE      Y0X0,TMP1           ;copy y0-y1:x0-x1
        SRA       16,TMP1             ;right justify y0-y1
        SUBXY     Y1X1,Y2X2           ;calculate y2-y1:x2-x1
        MOVE      Y2X2,TMP3           ;copy y2-y1:x2-x1
        SRA       16,TMP3             ;right justify y2-y1
        MPYS      Y2X2,TMP1           ;(y0-y1) * (x2-x1)
        MPYS      Y0X0,TMP3           ;(y2-y1) * (x0-x1)
        SUB       TMP3,TMP1           ;(y0-y1)*(x2-x1)-(y2-y1)*(x0-x1)
        JRNN      TRIV2D              ;jump if sum >= 0 (is back face)
; Copy list of vertex xy coord's, starting with topmost vertex in face.
        MOVE      YXTOP,*QTMP+,0      ;copy xy coord's at topmost vertex
        JR        COMP2D              ;
COPY2D:
        MOVE      *PTOP+,*QTMP+,0     ;copy xy coord's at next CW vertex
COMP2D:
        CMP       PTOP,STK            ;pointing past end of xy array?
        JRNE      OVER2D              ;jump if not past end of xy array
        MOVE      S,PTOP              ;else pointer = start of xy array
OVER2D:
        DSJ       NVRTS,COPY2D        ;copy all vertices in face
        MOVE      QTMP,Q              ;update output display list pointer
; Trivially reject flat or back-facing polygon.
TRIV2D:
        MOVE      S,STK               ;clean polygon xy coord's off stack
        JR        PUSH2D              ;extract next polygon from xy list
; Done with all faces of this object.  Ready to process next node in tree.
DONE2D:
        MOVE      T,STK               ;clean object xy coord's off stack

*-----------------------------------------------------------------------
; Interpret next 16-bit function code from input list.
*-----------------------------------------------------------------------
NEXTCMD1:
        SETF      16,1,0              ;set up for 16-bit memory moves
        SETF      32,0,1              ;set up for 32-bit memory moves
        MOVE      *P+,CMD,0           ;input next 16-bit function code
        JRZ       PUSHDOT1            ;if f.c. = 0, single dot
        DEC       CMD                 ;
        JRGT      XFRMFAR1            ;if f.c. = 2, "far" polygon
        JRZ       XFRMNEAR            ;if f.c. = 1, "near" polygon
        JR        EXIT                ;if f.c. = -1, exit interpreter

*-----------------------------------------------------------------------
* Simply copy description of dot from input list to output list.
*-----------------------------------------------------------------------
PUSHDOT1:

        SUBK      16,P                ;back up to dot function code
        MOVE      Q,QTMP              ;copy output list pointer
        MOVE      *P+,*QTMP+,1        ;copy dot f.c. and color
        MOVE      *P+,*QTMP+,1        ;copy dot x and y coordinates
        MOVE      QTMP,Q              ;update output list pointer
        JR        NEXTCMD1            ;more objects in input list?


*-----------------------------------------------------------------------
*                   Transform "Near" Graphics Object
*-----------------------------------------------------------------------
XFRMNEAR:

        MOVI      _vpclip+KX,PTMP     ;point to constant Kx
        MOVE      *PTMP+,KXX,1        ;get scaling constant Kx (FIX18)
        SRA       2,KXX               ;convert Kx to FIX16
        MOVE      *PTMP+,KYY,1        ;get scaling constant Ky (FIX18)
        SRA       2,KYY               ;convert Ky to FIX16
; Get "near" rotation matrix that has already been scaled to Kx and Ky.
        MOVI      6*32,T              ;get displacement to "near" matrix
        ADD       FMAT,T              ;point to matrix
        MMFM      T,M00,M01,M02,M10,M11,M12 ;FIX16 rotation coeff's

; Jump here if Kx, Ky and matrix elements m[i,j] already reside in
; registers.  Read polygon's parameters from input transform list.
XFRMNEAR1:
        ADDK      16,P                ;skip past constant 0
        MMFM      P,XVV,YVV,ZVV,OBJ   ;get xv, yv, zv and pointer obj
; The object's centroid coordinates in viewing space, xv, yv and zv,
; are in FIX8 format.  Scale xv and yv to normalized clipping volume.
        MOVE      KXX,TMP0            ;copy clip vol x normalize factor
        MPYS      XVV,TMP0            ;xv * Kx (FIX8 * FIX16)
        MOVY      TMP1,TMP0           ;
        RL        16,TMP0             ;convert xv' = xv * Kx to FIX8
        MOVE      TMP0,XVV            ;save xv'
        MOVE      KYY,TMP2            ;copy clip vol y normalize factor
        MPYS      YVV,TMP2            ;yv * Ky (FIX8 * FIX16)
        MOVY      TMP3,TMP2           ;
        RL        16,TMP2             ;convert yv' = yv * Ky to FIX8
        MOVE      TMP2,YVV            ;save yv'

; Database gives points of 3D object as xyz displacements from object's
; centroid coordinates, xv, yv and zv.  The xyz displacements are 8-bit
; values, X, Y and Z; they are accompanied by an 8-bit scaling factor,
; F, in the range 0 to 7.  All are packed into a single 32-bit longword.
        MOVE      STK,T               ;keep pointer to transformed xyz's
        MOVE      *OBJ+,A8,0          ;get number of points to transform
        MOVE      A8,NPTS             ;NPTS = number of points in object
        SETF      8,1,1               ;set field size for 8-bit multiplier
        SETF      32,0,0              ;set up for 32-bit memory moves
NEARLOOP:
        MOVE      *OBJ+,FZYX,0        ;get X, Y, Z and F (X in 8 LSBs)

        MOVE      M00,TMP5            ;get rotation matrix coeff. M00 (FIX16)
        MPYS      FZYX,TMP5           ;x * M00 (FIX16)

        MOVE      M10,TMP3            ;get rotation matrix coeff. M10 (FIX16)
        MPYS      FZYX,TMP3           ;x * M10 (FIX16)

        MOVE      M20,TMP1            ;get rotation matrix coeff. M20 (FIX16)
        MPYS      FZYX,TMP1           ;x * M20 (FIX16)

        SRL       8,FZYX              ;shift Y into 8 LSBs of multiplier
        MOVE      M01,TMP7            ;get rotation matrix coeff. M01 (FIX16)
        MPYS      FZYX,TMP7           ;y * M01 (FIX16)
        ADD       TMP7,TMP5           ;x * M00 + y * M01

        MOVE      M11,TMP7            ;get rotation matrix coeff. M11 (FIX16)
        MPYS      FZYX,TMP7           ;y * M11 (FIX16)
        ADD       TMP7,TMP3           ;x * M10 + y * M11

        MOVE      M21,TMP7            ;get rotation matrix coeff. M21 (FIX16)
        MPYS      FZYX,TMP7           ;y * M21 (FIX16)
        ADD       TMP7,TMP1           ;x * M20 + y * M21

        SRL       8,FZYX              ;shift Z into 8 LSBs of multiplier
        MOVE      M02,TMP7            ;get rotation matrix coeff. M02 (FIX16)
        MPYS      FZYX,TMP7           ;z * M02 (FIX16)
        ADD       TMP7,TMP5           ;x * M00 + y * M01 + z * M02 (FIX16)

        MOVE      M12,TMP7            ;get rotation matrix coeff. M12 (FIX16)
        MPYS      FZYX,TMP7           ;z * M12 (FIX16)
        ADD       TMP7,TMP3           ;x * M10 + y * M11 + z * M12 (FIX16)

        MOVE      M22,TMP7            ;get rotation matrix coeff. M22 (FIX16)
        MPYS      FZYX,TMP7           ;z * M22 (FIX16)
        ADD       TMP7,TMP1           ;x * M20 + y * M21 + z * M22 (FIX16)

        SRL       8,FZYX              ;shift log2(factor) into 8 LSBs
        SUBK      8,FZYX              ;combine with FIX8 scaling factor
        SRA       FZYX,TMP5           ;convert x*M00+y*M01+z*M02 to FIX8
        ADD       XVV,TMP5            ;x' = xv+x*M00+y*M01+z*M02 (FIX8)

        SRA       FZYX,TMP3           ;convert x*M10+y*M11+z*M12 to FIX8
        ADD       YVV,TMP3            ;y' = yv+x*M10+y*M11+z*M12 (FIX8)

        SRA       FZYX,TMP1           ;convert x*M20+y*M21+z*M22 to FIX8
        ADD       ZVV,TMP1            ;z' = zv+x*M20+y*M21+z*M22 (FIX8)

        MOVE      TMP5,*STK+,0        ;save x'
        MOVE      TMP3,*STK+,0        ;save y'
        MOVE      TMP1,*STK+,0        ;save z'
        DSJ       NPTS,NEARLOOP       ;more 3D points to transform?

*------------------------------------------------------------------------------
* Append description of one or more polygons to output (display) list.
*------------------------------------------------------------------------------
        SETF      32,0,1              ;set up for 32-bit memory moves
        SETF      16,1,0              ;set up for 16-bit memory moves
        MOVE      STK,S               ;save base address of xy array
; Get polygon vertex count and color.
PUSH3D:
        MOVE      *OBJ+,NVRTS,0       ;get no. of vertices in this face
        JRZ       DONE3D              ;jump if end of data encountered
        MOVE      *OBJ+,PCOLOR,0      ;get polygon COLOR code
; Allocate enough space on program stack to contain clipped xy array.
        MOVE      NVRTS,TMP1          ;leave room on stack for xy array
        ADDK      5,TMP1              ;clipping may add up to 5 vertices
        SLL       5,TMP1              ;convert to longword offset
        ADD       TMP1,STK            ;point past end of xy array
; Call _clippgon function to clip 3D polygon and project onto 2D screen.
        MOVE      STK,-*SP,1          ;push program stack pointer
        MOVE      S,PTMP              ;get pointer to xyz coord's
        MOVE      PTMP,*STK+,1        ;push arg xy (output coord's ptr)
        MOVE      OBJ,*STK+,1         ;push arg OBJ (input vertex ptr)
        MOVE      T,TMP0              ;get pointer to xyz coord's
        MOVE      TMP0,*STK+,1        ;push arg xyz (input coord's ptr)
        MOVE      NVRTS,*STK+,1       ;push argument NVRTS (vertex count)
        CALLA     _clippgon           ;clip 3D polygon to 2D screen
        SETF      16,1,0              ;set up for 16-bit memory moves
        SLL       4,NVRTS             ;convert vertex count to wd offset
        ADD       NVRTS,OBJ           ;advance pointer past vertex list
        MOVE      A8,NVRTS            ;copy vertex count for clipped polygon
        SUBK      3,A8                ;make sure vertex count >= 3
        JRN       TRIV3D              ;jump if vertex count < 3
; Search xy coordinate list for topmost vertex (minimum y) in polygon.
        MOVI      03FFF0000h,YXTOP    ;initialize ymin = max y value
        MOVE      NVRTS,COUNT         ;initialize loop counter
TOP3D:
        MOVE      *PTMP+,YX,1         ;get x and y coordinates at vertex
        CMPXY     YX,YXTOP            ;is y < ymin ?
        JRYZ      SKIP3D              ;jump if y == ymin
        MOVE      COUNT,NTOP          ;make copy of current loop count
        JRYN      SKIP3D              ;jump if y > ymin
        MOVE      YX,YXTOP            ;set ymin = y
        MOVE      PTMP,PTOP           ;copy pointer (autoincremented)
SKIP3D:
        DSJ       COUNT,TOP3D         ;loop if more vertices

; Coordinates of topmost vertex in polygon have been found.
        CMP       NVRTS,NTOP          ;are all vertices at same y?
        JREQ      TRIV3D              ;jump to trivial reject flat face
; Append DRAWCODE, NVRTS and COLOR to output (display) list.
        MOVE      Q,QTMP              ;update display list pointer
        SLL       8,NVRTS             ;NVRTS in 8 LSBs of word
        INC       NVRTS               ;DRAWCODE=1 (polygon) in 8 LSBs
        MOVE      NVRTS,*QTMP+,0      ;append vertex count NVRTS to list
        SRL       8,NVRTS             ;right justify NVRTS
        MOVE      PCOLOR,*QTMP+,0     ;append polygon COLOR code to list
; Copy list of vertex xy coord's, starting with topmost vertex in face.
        MOVE      YXTOP,*QTMP+,1      ;copy xy coord's at first vertex
        JR        COMP3D              ;
COPY3D:
        MOVE      *PTOP+,*QTMP+,1     ;append xy coord pair to list
COMP3D:
        CMP       PTOP,PTMP           ;pointing past end of xy array?
        JRNE      OVER3D              ;jump if not past end of xy array
        MOVE      S,PTOP              ;else pointer = start of xy array
OVER3D:
        DSJ       NVRTS,COPY3D        ;loop through all vertices in face

        MOVE      QTMP,Q              ;update display list pointer
; Trivially reject flat or back-facing polygon.
TRIV3D:
        MOVE      S,STK               ;clean polygon xy list off stack
        JR        PUSH3D              ;extract next polygon from xyz list
; Done with all faces of this object.  Ready to process next node in tree.
DONE3D:
        MOVE      T,STK               ;clean object xyz list off stack


*-----------------------------------------------------------------------
; Interpret next 16-bit function code from input list.
*-----------------------------------------------------------------------
NEXTCMD2:
        MOVE      *P+,CMD,0           ;input next 16-bit function code
        JRZ       PUSHDOT2            ;if f.c. = 0, single dot
        DEC       CMD                 ;
        JRGT      XFRMFAR             ;if f.c. = 2, "far" polygon
        JRZ       XFRMNEAR1           ;if f.c. = 1, "near" polygon
        JR        EXIT                ;if f.c. = -1, exit interpreter


*-----------------------------------------------------------------------
* Simply copy description of dot from input list to output list.
*-----------------------------------------------------------------------
PUSHDOT2:

        SUBK      16,P                ;back up to dot function code
        MOVE      Q,QTMP              ;copy output list pointer
        MOVE      *P+,*QTMP+,1        ;copy dot f.c. and color
        MOVE      *P+,*QTMP+,1        ;copy dot x and y coordinates
        MOVE      QTMP,Q              ;update output list pointer
        JR        NEXTCMD2            ;more objects in input list?


*----------------------------------------------------------------------
*                   Exit transform list interpreter
*----------------------------------------------------------------------
EXIT:

        MOVI      -1,COUNT            ;end-of-list function code = -1
        MOVE      COUNT,*Q+,0         ;append EOL f.c. to output list
        MOVE      Q,A8                ;return ptr to end of output list
        MMFM      SP,B0,B1,B2,B3,B4,B5,B6,B7,B8,B9,B10,B11,B12,B13,B14
        MMFM      SP,A0,A1,A2,A3,A4,A5,A6,A7,A9,A10,A11,A12,A13
        MOVE      *SP(32),STK,1       ;update program stack pointer
        RETS      2                   ;

        .end







