        .width    132
        .title    'clip 3D polygon'

*----------------------------------------------------------------------
*                                    TIGA
*          Copyright (C) 1989-1990  Texas Instruments Incorporated.
*                            All Rights Reserved
*----------------------------------------------------------------------
*  clippgon function
*
*    Clip a convex 3D polygon to canonical clipping volume.  A polygon
*    is represented as a series of vertices in the form [x y z], where
*    x, y and z are coordinates in the viewing space.  The conventions
*    used for viewing coordinates are that the origin lies in the center
*    of the viewport, the +x direction is from left to right, the +y
*    direction is from top to bottom, and the +z direction is into the
*    screen.  All vertices are assumed to lie in the same plane.
*
*    Both the input and (clipped) output vertices are represented by
*    fixed point coordinates.  The value returned is the number of
*    vertices in the clipped polygon.
*
*    Prior to calling this function, the x and y coordinates are scaled
*    to convert the viewing pyramid to a right pyramid (a "canonical
*    clipping volume", as described in Foley & van Dam).  As a result of
*    this scaling, the viewing pyramid is bounded at the top and bottom
*    by the planes z-y=0 and z+y=0.  Similarly, the viewing pyramid is
*    bounded at the right and left by the planes z-x=0 and z+x=0.  A
*    "near" (or "hither") clipping plane is defined at some z = Znear.
*    The "far" (or "yon") z-clipping plane is assumed to lie at
*    +infinity.
*
*    A backface check is performed.  If the polygon is determined to be
*    facing away from the viewer, it is trivially rejected.
*
*    The calling routine passes four arguments.  The first is an integer
*    specifying the number of vertices, nverts, in the polygon to be
*    clipped.
*
*    The second argument, inlist, is an array of 32-bit coordinates
*    representing the vertices of the original, unclipped 3D polygon.
*    Each vertex is stored as three adjacent 32-bit values, x, y and z.
*    Each value is represented as a fixed-point number with 8 bits of
*    fraction.
*
*    The third argument, connect, is an array of 16-bit indices into the
*    inlist array.  The connect array specifies the order in which the
*    vertices in the inlist array are traversed in moving around the
*    perimeter of the polygon.  Each index in the connect array
*    identifies the particular xyz-coordinate triple stored at address
*    (inlist + 3 * 32 * index).  The indices in the connect array are
*    specified in the order in which the corresponding vertices (0, 1,
*    2, etc.) are traversed moving in clockwise order around the
*    front-facing side of the polygon.  (The term "clockwise" in this
*    context assumes that x increases from left to right on the screen,
*    and that y increases from top to bottom.)
*
*    The fourth argument, outlist, is an array of 16-bit coordinates
*    representing the vertices of the resulting clipped 2D polygon.
*    Each vertex is represented as a 16-bit x coordinate followed by a
*    16-bit y coordinate.  Each value is represented as a fixed-point
*    number with 2 bits of fraction.  The vertices are listed in
*    clockwise order.
*-----------------------------------------------------------------------
*  Usage:  clippgon(nverts, inlist, connect, outlist);
*
*  Stack arguments:
*      int nverts;       /* number of vertices in polygon       */
*      long *inlist;     /* unclipped input [x y z] coordinates */
*      short *connect;   /* polygon vertex connectivity list    */
*      short *outlist;   /* clipped output [x' y'] coordinates  */
*
*  Returned in A8:  The number of vertices in the clipped polygon
*
*  Registers altered:  A8
*-----------------------------------------------------------------------
*  Revision History:
*      01/31/89...Original version written................Jerry Van Aken
*-----------------------------------------------------------------------
*
*
*   DECLARE GLOBAL FUNCTION NAME
*
        .globl    _clippgon           ;global function entry point
*
*
*   DECLARE EXTERNAL VARIABLES
*
        .globl    _vpclip             ;viewport clipping parameters
*
*
*   DEFINE CONSTANTS
*
        .nolist
        .copy     "fly3d.inc"         ;define flight sim. structures
        .list
; Number of bits of fraction in fixed-point numbers
FSIZE   .set      8                   ;32-bit no. with 8-bit fraction
; Symbolic A- and B-file register names
X2      .set      A0                  ;x coordinate at second vertex
X1      .set      A1                  ;x coordinate at first vertex
Y2      .set      A2                  ;y coordinate at second vertex
Y1      .set      A3                  ;y coordinate at first vertex
Z2      .set      A4                  ;z coordinate at second vertex
Z1      .set      A5                  ;z coordinate at first vertex
CLIP    .set      A6                  ;pointer to clip params structure
T0      .set      A8                  ;even half of temp. reg. pair T
XSCALE  .set      A8                  ;convert normalized x to viewport
T1      .set      A9                  ;odd half of temp. reg. pair T
YSCALE  .set      A9                  ;convert normalized y to viewport
U0      .set      A10                 ;even half of temp. reg. pair U
XCTR    .set      A10                 ;viewport center x coordinate
U1      .set      A11                 ;odd half of temp. reg. pair U
YCTR    .set      A11                 ;viewport center y coordinate
INLIST  .set      A11                 ;address of list of input coord's
CONNECT .set      A12                 ;address of vertex connect list
PIN     .set      A12                 ;input coordinate pointer
NEAR    .set      A13                 ;z value at "near" clipping plane
POUT    .set      A14                 ;output coordinate pointer

OUTCODE .set      B0                  ;outcode for all 5 clipping planes
OC1     .set      B0                  ;outcode for first vertex
ANDCODE .set      B1                  ;AND of outcodes for all vertices
OC2     .set      B1                  ;outcode for second vertex
ORCODE  .set      B2                  ;OR of outcodes for all vertices
COUNT   .set      B3                  ;loop count
NVERTS  .set      B4                  ;number of vertices in polygon
OUTLIST .set      B5                  ;address of output coordinate list
BUFA    .set      B6                  ;temp. input coordinate buffer
BUFB    .set      B7                  ;temp. output coordinate buffer
BTEMP   .set      B8                  ;temporary register
*
*
*   ENTRY POINT
*
_clippgon:

        SETF      16,1,0              ;set up for 16-bit accesses
        MMTM      SP,A0,A1,A2,A3,A4,A5,A6,A9,A10,A11,A12,A13
        MMTM      SP,B0,B1,B2,B3,B4,B5,B6,B7,B8
; Pop all 4 arguments off stack.
        MOVE      *-A14,T0,1          ;get argument nverts
        MOVE      T0,NVERTS           ;
        SUBK      3,T0                ;check for min. no. vertices
        JRLT      TRIVIAL             ;dpme if nverts < 3
        MOVE      *-A14,INLIST,1      ;get argument inlist
        MOVE      *-A14,CONNECT,1     ;get argument connect
        MOVE      *-A14,T0,1          ;get argument outlist
        MOVE      T0,OUTLIST          ;
; Set up to check which clipping planes are violated by polygon.
        MOVI      _vpclip,CLIP        ;pointer to CLIPPARMS struct
        MOVE      *CLIP(ZNEAR),NEAR,0 ;get Znear
        SLL       8,NEAR              ;convert integer to FIX8 format
        MOVE      *CLIP(BUF0),POUT,1  ;get address of temp buffer 0
        MOVE      POUT,BUFA           ;set up pointer to temp buffer
        CLR       ORCODE              ;initialize ORcode
        MOVI      63,ANDCODE          ;initialize ANDcode
        MOVE      NVERTS,COUNT        ;initialize vertex count

*-----------------------------------------------------------------------
* Each loop below compares one vertex against all 5 clipping planes.
*-----------------------------------------------------------------------
LOOP1:
        CLR       OUTCODE             ;initialize outcode for vertex
; Get [x y z w] coordinates for next vertex.
        MOVE      *CONNECT+,T0,0      ;get next index into inlist list
        MOVE      T0,T1               ;
        ADD       T1,T0               ;
        ADD       T1,T0               ;3*index
        SLL       5,T0                ;convert to 32-bit offset
        ADD       INLIST,T0           ;address = &inlist[3*index]
        MOVE      *T0+,X1,1           ;read x
        MOVE      *T0+,Y1,1           ;read y
        MOVE      *T0+,Z1,1           ;read z
; Compare z against the "near" clipping plane:  z < Znear ?
        CMP       NEAR,Z1             ;is z < Znear ? (outside?)
        JRGE      L1                  ;jump if z >= znear (inside)
        ADDK      16,OUTCODE          ;note violation of z=Znear plane
L1:
        NEG       Z1                  ;-z
; Compare y against bottom clipping plane:  -z <= y ?
        CMP       Z1,Y1               ;is -z > y ?
        JRGE      L2                  ;jump if -z <= y
        ADDK      4,OUTCODE           ;note violation of z+y=0 plane
L2:
; Compare x against left clipping plane:  -z <= x ?
        CMP       Z1,X1               ;is -z > x ?
        JRGE      L3                  ;jump if -z <= x
        ADDK      1,OUTCODE           ;note violation of z+x=0 plane
L3:
; Compare y against top clipping plane:  y <= +z ?
        NEG       Z1                  ;+z
        CMP       Z1,Y1               ;is y > +z ?
        JRLE      L4                  ;jump if y <= +z
        ADDK      8,OUTCODE           ;note violation of z-y=0 plane
L4:
; Compare x against right clipping plane:  x <= +z ?
        CMP       Z1,X1               ;is x > +z ?
        JRLE      L5                  ;jump if x <= +z
        ADDK      2,OUTCODE           ;note violation of z-x=0 plane
L5:
; Store z, y and x in output list, and update ORcode and ANDcode.
        MOVE      X1,*POUT+,1         ;store x coordinate
        MOVE      Y1,*POUT+,1         ;store y coordinate
        MOVE      Z1,*POUT+,1         ;store z coordinate
        OR        OUTCODE,ORCODE      ;ORcode |= outcode
        AND       OUTCODE,ANDCODE     ;ANDcode &= outcode
        DSJ       COUNT,LOOP1         ;loop if more vertices

*-----------------------------------------------------------------------
* Trivially reject polygon if it is 100% outside clipping volume.
*-----------------------------------------------------------------------
; If ANDcode != 0, trivially reject polygon (it is not visible).
        JRZ       BACKFACE            ;jump if ANDcode is 0
        CLR       NVERTS              ;return value nverts = 0
        JRUC      DONE                ;all done--polygon not visible

*-----------------------------------------------------------------------
* Perform 3D backface check with first vertex (v0) as pivot point.
*-----------------------------------------------------------------------
BACKFACE:
        MOVE      BUFA,PIN            ;point to start of coordinate list
        ADDI      6*32,PIN            ;point past vertices 0 and 2
        MOVE      -*PIN,Z2,1          ;get z2
        MOVE      -*PIN,Y2,1          ;get y2
        MOVE      -*PIN,X2,1          ;get x2
        MOVE      Y2,T0               ;copy y2
        MOVE      Y1,U0               ;copy y1 (still valid from LOOP1)
        MPYS      Z1,T0               ;y2 * z1
        MPYS      Z2,U0               ;y1 * z2
        SUB       U1,T1               ;
        SUBB      U0,T0               ;y2 * z1 - y1 * z2
        MOVY      T1,T0               ;
        RL        16,T0               ;truncate result to 32 bits
        MOVE      T0,OC1              ;save y2 * z1 - y1 * z2

        MOVE      X1,T0               ;get x1 (still valid from LOOP1)
        MOVE      X2,U0               ;get x2
        MPYS      Z2,T0               ;x1 * z2
        MPYS      Z1,U0               ;x2 * z1
        SUB       U1,T1               ;
        SUBB      U0,T0               ;x1 * z2 - x2 * z1
        MOVY      T1,T0               ;
        RL        16,T0               ;truncate result to 32 bits
        MOVE      T0,OC2              ;save x1 * z2 - x2 * z1

        MOVE      X2,T0               ;get x2
        MOVE      X1,U0               ;get x1 (still valid from LOOP1)
        MPYS      Y1,T0               ;x2 * y1
        MPYS      Y2,U0               ;x1 * y2
        SUB       U1,T1               ;
        SUBB      U0,T0               ;x2 * y1 - x1 * y2
        MOVY      T1,T0               ;
        RL        16,T0               ;truncate result to 32 bits

        MOVE      -*PIN,Z2,1          ;get z0
        MOVE      -*PIN,Y2,1          ;get y0
        MOVE      -*PIN,X2,1          ;get x0

        MPYS      Z2,T0               ;z0 * (x2 * y1 - x1 * y2)
        MOVE      OC2,U0              ;copy x1 * z2 - x2 * z1
        MPYS      Y2,U0               ;y0 * (x1 * z2 - x2 * z1)
        ADD       U1,T1               ;
        ADDC      U0,T0               ;y0*(x1*z2-x2*z1)+z0*(x2*y1-x1*y2)
        MOVE      OC1,U0              ;copy y2 * z1 - y1 * z2
        MPYS      X2,U0               ;x0 * (y2 * z1 - y1 * z2)
        ADD       U1,T1               ;
        ADDC      U0,T0               ;
        JRNN      FACING              ;jump if facing forward
TRIVIAL:
        CLR       NVERTS              ;is backface--cull it
        JRUC      DONE                ;all done

*-----------------------------------------------------------------------
* Does polygon need to be trimmed to fit canonical clipping volume?
*-----------------------------------------------------------------------
FACING:
        SETF      32,0,0              ;set up for 32-bit accesses
        MOVE      ORCODE,COUNT        ;copy ORcode
        JRNZ      NEEDCLIP            ;jump if clipping is required

; Polygon has been clipped to all 5 bounding planes.
CLIPPED:
        SETF      16,1,1              ;set up for 16-bit multipliers
        MOVE      *CLIP(SX),XSCALE,1  ;copy viewport half width Sx
        SLL       2,XSCALE            ;fixed pt with 4-bit fraction
        MOVE      *CLIP(SY),YSCALE,1  ;copy viewport half height Sy
        SLL       2,YSCALE            ;fixed pt with 4-bit fraction
        MOVE      *CLIP(CX),XCTR,1    ;copy x center coordinate Cx
        SLL       2,XCTR              ;fixed pt with 4-bit fraction
        MOVE      *CLIP(CY),YCTR,1    ;copy y center coordinate Cy
        SLL       2,YCTR              ;fixed pt with 4-bit fraction
        MOVE      BUFA,PIN            ;pointer to input coord. list
        MOVE      OUTLIST,POUT        ;pointer to output coord. list
        MOVE      NVERTS,COUNT        ;initialize loop count = nverts
        JRZ       DONE                ;done if vertex count = 0

*-----------------------------------------------------------------------
* Store vertices of clipped polygon into output coordinate list.
*-----------------------------------------------------------------------
LOOP2:
        MOVE      *PIN+,X1,0          ;read x
        MOVE      *PIN+,Y1,0          ;read y
        MOVE      *PIN+,Z1,0          ;read z
        MPYS      XSCALE,X1           ;Sx * x
        DIVS      Z1,X1               ;
        ADD       XCTR,X1             ;x ' = (Sx * x) / z + Cx
        MOVE      X1,*POUT+,1         ;store x' in output list
        MPYS      YSCALE,Y1           ;Sy * y
        DIVS      Z1,Y1               ;
        ADD       YCTR,Y1             ;y ' = (Sy * y) / z + Cy
        MOVE      Y1,*POUT+,1         ;store y' in output list
        DSJ       COUNT,LOOP2         ;more vertices to output?
*-----------------------------------------------------------------------

; All done.  Restore registers and return to calling routine.
DONE:
        SETF      32,0,1              ;restore field size 1
        MOVE      NVERTS,A8           ;set return value = nverts
        MMFM      SP,B0,B1,B2,B3,B4,B5,B6,B7,B8
        MMFM      SP,A0,A1,A2,A3,A4,A5,A6,A9,A10,A11,A12,A13
        MOVE      *SP(32),A14,1       ;restore STK pointer
        RETS      2                   ;


*-----------------------------------------------------------------------
* Polygon needs to be clipped to one or more bounding planes.
*-----------------------------------------------------------------------
NEEDCLIP:

        SETF      18,1,1              ;set up for 18-bit multipliers
; Reformat ORcode for easier decoding.
        SRL       1,COUNT             ;OR bits 0 and 1, and bits 2 and 3
        OR        COUNT,ORCODE        ;now need to check only bits 0,2,4
; Initialize pointers to temp. input and output buffers for xyz coords.
        MOVE      BUFA,PIN            ;set up pointer to input buffer
        MOVE      CLIP,BUFB           ;copy pointer to clip params
        MOVE      *BUFB(BUF1),BUFB,0  ;output buffer base address
; Initialize outcode for vertex 1.
        CLR       OC1                 ;
; Test 1:  Does polygon violate the "near" clipping plane?
        BTST      4,ORCODE            ;is "near" plane violated?
        JRZ       YCHECK              ;jump if polygon inside near plane
; Need to clip polygon to "near" bounding plane.  Assume that the values
; [x1 y1 z1] left in A-file registers from LOOP1 are still valid.
        MOVE      NVERTS,COUNT        ;number of vertices in input list
        CLR       NVERTS              ;initial length of output list = 0
        JRUC      ZCLIP               ;clip to "near" plane
ZBACK:
        MOVE      BUFB,PIN            ;reinitialize input pointer
        MOVE      BUFA,BUFB           ;
        MOVE      PIN,BUFA            ;swap input and output buffers
; Test 2:  Does polygon violate the top or bottom clipping plane?
YCHECK:
        BTST      2,ORCODE            ;top or bottom plane violated?
        JRZ       XCHECK              ;jump if polygon inside both planes
; Need to clip polygon to top and/or bottom bounding planes.
        MOVE      NVERTS,COUNT        ;number of vertices in input list
        JRZ       DONE                ;all done if vertex count = 0
        CLR       NVERTS              ;initial length of output list = 0
        MOVE      OC1,OC1             ;is previous vertex 1 still valid?
        JRNZ      YCLIP               ;no, need to read in new x1,y1,z1
        JRUC      YCLIP1              ;yes, old vertex 1 is still valid
YBACK:
        MOVE      BUFB,PIN            ;reinitialize input pointer
        MOVE      BUFA,BUFB           ;
        MOVE      PIN,BUFA            ;swap input and output buffers
; Test 3:  Does polygon violate the left or right clipping plane?
XCHECK:
        BTST      0,ORCODE            ;left or right plane violated?
        JRZ       CLIPPED             ;jump if polygon inside both planes
; Need to clip polygon to left and/or right bounding planes.
        MOVE      NVERTS,COUNT        ;number of vertices in input list
        JRZ       DONE                ;all done if vertex count = 0
        CLR       NVERTS              ;initial length of output list = 0
        MOVE      OC1,OC1             ;is previous vertex 1 still valid?
        JRNZ      XCLIP               ;no, need to read in new x1,y1,z1
        JRUC      XCLIP1              ;yes, old vertex 1 is still valid
XBACK:
        MOVE      BUFB,BUFA           ;new input buffer = old output buf
        JRUC      CLIPPED             ;finished clipping polygon


*-----------------------------------------------------------------------
*
*                    Clip to "near" bounding plane.
*
*-----------------------------------------------------------------------
ZCLIP:

        MOVE      BUFB,POUT           ;set up pointer to output buffer
; Initial [x1 y1 z1 z1] are still in A-file registers from LOOP1.
; Compute outcode for vertex 1 based on whether z1 violates clip plane.
        CMP       NEAR,Z1             ;is z1 < Znear ? (outside?)
        JRGE      ZC1                 ;jump if z1 >= Znear (inside)
        MOVK      1,OC1               ;set outcode1 = 1 (outside Znear)
ZC1:

*-----------------------------------------------------------------------
* Each iteration below clips (if necessary) one polygon edge.
*-----------------------------------------------------------------------
ZLOOP:
        MOVE      *PIN+,X2,0          ;get x2 from input list
        MOVE      *PIN+,Y2,0          ;get y2 from input list
        MOVE      *PIN+,Z2,0          ;get z2 from input list
; Compute outcode for vertex 2 based on whether z2 violates clip plane.
        CLR       OC2                 ;initialize outcode2 = 0
        CMP       NEAR,Z2             ;is z2 < Znear ? (outside?)
        JRGE      ZL1                 ;jump if z2 >= Znear (inside)
        MOVK      1,OC2               ;set outcode2 = 1 (outside Znear)
ZL1:
; Are vertices 1 and 2 are both out?  If so, entire edge is invisible.
        MOVE      OC1,BTEMP           ;copy outcode1
        AND       OC2,BTEMP           ;outcode1 & outcode2
        JRNZ      ZL5                 ;jump if both vertices out on same side
; Now we know that vertices 1 and 2 are not both outside the "near"
; plane.  Is vertex 1 outside the "near" clipping plane?  If so, clip.
        BTST      0,OC1               ;is vertex 1 outside "near" clip plane?
        JRNZ      Z1OUT               ;jump if vertex 1 outside plane
; Is vertex 2 outside the "near" clipping plane?  If so, clip.
ZL2:
        BTST      0,OC2               ;is vertex 2 outside "near" clip plane?
        JRNZ      Z2OUT               ;jump if vertex 2 outside plane
* z2 is inside the "near" clipping plane.
ZL3:
        MOVE      X2,*POUT+,0         ;store x2
        MOVE      Y2,*POUT+,0         ;store y2
        MOVE      Z2,*POUT+,0         ;store z2
; Increment count of output vertices.
ZL4:
        INC       NVERTS              ;
* Jump here if vertices 1 and 2 are both outside same clipping plane.
ZL5:
        MOVE      X2,X1               ;x1 = x2
        MOVE      Y2,Y1               ;y1 = y2
        MOVE      Z2,Z1               ;z1 = z2
        MOVE      OC2,OC1             ;outcode1 = outcode2
        DSJ       COUNT,ZLOOP         ;loop if more vertices
        JRUC      ZBACK               ;otherwise, return

*-----------------------------------------------------------------------
* Clip edge from vertex 1 to vertex 2 to "near" bounding plane.
*-----------------------------------------------------------------------
Z1OUT:

; Calculate value of independent parameter t to clip to "near" plane.
        MOVE      NEAR,T1             ;
        SUB       Z1,T1               ;v = Znear - z1
        MOVE      Z2,U0               ;
        SUB       Z1,U0               ;u = z2 - z1
        MOVE      T1,T0               ;
        SRA       16,T0               ;calculate (v << 16) to 64 bits
        SLL       16,T1               ;
        DIVS      U0,T0               ;t = (v<<16) / u (16-bit fraction)
; Use parameter t to clip x1 to "near" plane.
        MOVE      X2,U0               ;
        SUB       X1,U0               ;
        MPYS      T0,U0               ;t * (x2 - x1)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      X1,U1               ;x1 ' = x1 + (t * (x2-x1) >> 16)
        MOVE      U1,*POUT+,0         ;store x1' in temp. output buffer
; Clip y1 to "near" plane.
        MOVE      Y2,U0               ;
        SUB       Y1,U0               ;
        MPYS      T0,U0               ;t * (y2 - y1)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      Y1,U1               ;y1 ' = y1 + (t * (y2-y1) >> 16)
        MOVE      U1,*POUT+,0         ;store y1' in temp. output buffer
; Clip z1 to "near" plane.
        MOVE      NEAR,*POUT+,0       ;store z1'= Znear in output buffer
; Increment count of output vertices.
        INC       NVERTS              ;
        JRUC      ZL3                 ;jump past test of outcode2

*-----------------------------------------------------------------------
* Clip edge from vertex 2 to vertex 1 to "near" bounding plane.
*-----------------------------------------------------------------------
Z2OUT:

; Calculate value of independent parameter t to clip to "near" plane.
        MOVE      NEAR,T1             ;
        SUB       Z2,T1               ;v = Znear - z2
        MOVE      Z1,U0               ;
        SUB       Z2,U0               ;u = z1 - z2
        MOVE      T1,T0               ;
        SRA       16,T0               ;calculate (v << 16) to 64 bits
        SLL       16,T1               ;
        DIVS      U0,T0               ;t = (v<<16) / u (16-bit fraction)
; Use parameter t to clip x2 to "near" plane.
        MOVE      X1,U0               ;
        SUB       X2,U0               ;
        MPYS      T0,U0               ;t * (x1 - x2)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      X2,U1               ;x2 ' = x2 + (t * (x1-x2) >> 16)
        MOVE      U1,*POUT+,0         ;store x2' in temp. output buffer
; Clip y2 to "near" plane.
        MOVE      Y1,U0               ;
        SUB       Y2,U0               ;
        MPYS      T0,U0               ;t * (y1 - y2)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      Y2,U1               ;y2 ' = y2 + (t * (y1-y2) >> 16)
        MOVE      U1,*POUT+,0         ;store y2' in temp. output buffer
; Clip z2 to "near" plane.
        MOVE      NEAR,*POUT+,0       ;store z2'= Znear in output buffer
        JRUC      ZL4                 ;


*-----------------------------------------------------------------------
*
*               Clip to top and bottom bounding planes.
*
*-----------------------------------------------------------------------
YCLIP:

        CLR       OC1                 ;initialize outcode1 = 0
; Read back x1,y1,z1 from end of previous output list.
        MOVE      -*POUT,Z1,0         ;read z1
        MOVE      -*POUT,Y1,0         ;read y1
        MOVE      -*POUT,X1,0         ;read x1
; Jump here if [x1 y1 z1 z1] are still valid from before.
YCLIP1:
        MOVE      BUFB,POUT           ;set up pointer to output buffer
        NEG       Z1                  ;get -z1
; Compute outcode for vertex 1 based on whether y1 violates clip planes.
        CMP       Z1,Y1               ;is -z1 > y1 ? (outside?)
        JRGE      YC1                 ;jump if -z1 <= y1 (inside)
        MOVK      1,OC1               ;set outcode1 = 1
YC1:
        NEG       Z1                  ;get +z2
        CMP       Z1,Y1               ;is +z1 < y1 ? (outside?)
        JRLE      YLOOP               ;jump if +z1 >= y1 (inside)
        MOVK      2,OC1               ;set outcode1 = 2

*-----------------------------------------------------------------------
* Each iteration below clips (if necessary) one polygon edge.
*-----------------------------------------------------------------------
YLOOP:
        MOVE      *PIN+,X2,0          ;get x2 from input list
        MOVE      *PIN+,Y2,0          ;get y2 from input list
        MOVE      *PIN+,Z2,0          ;get z2 from input list
        NEG       Z2                  ;get -z2
; Compute outcode for vertex 2 based on whether y2 violates clip planes.
        CLR       OC2                 ;initialize outcode2 = 0
        CMP       Z2,Y2               ;is -z2 > y2 ? (outside w+y=0 plane?)
        JRGE      YL1                 ;jump if -z2 <= y2 (inside)
        MOVK      1,OC2               ;set outcode2 = 1
YL1:
        NEG       Z2                  ;+z2
        CMP       Z2,Y2               ;is +z2 < y2 ? (outside w-y=0 plane?)
        JRLE      YL2                 ;jump if +z2 >= y2 (inside)
        MOVK      2,OC2               ;set outcode2 = 2
YL2:
        MOVE      OC1,BTEMP           ;copy outcode1
        AND       OC2,BTEMP           ;outcode1 & outcode2
        JRNZ      YL5                 ;jump if both vertices out on same side
; Is vertex 1 outside top or bottom clipping plane?  If so, clip.
        MOVE      OC1,OC1             ;is vertex 1 outside top/bottom plane?
        JRNZ      Y1OUT               ;jump if vertex 1 outside plane
; Is vertex 2 outside top or bottom clipping plane?  If so, clip.
YL3:
        MOVE      OC2,OC2             ;is vertex 2 outside top/bottom plane?
        JRNZ      Y2OUT               ;jump if vertex 2 outside plane
; Coordinate y2 is inside both the top and bottom clipping planes.
        MOVE      X2,*POUT+,0         ;store x2
        MOVE      Y2,*POUT+,0         ;store y2
        MOVE      Z2,*POUT+,0         ;store z2
; Increment count of output vertices.
YL4:
        INC       NVERTS              ;
; Jump here if vertices 1 and 2 both outside the same clipping plane.
YL5:
        MOVE      X2,X1               ;x1 = x2
        MOVE      Y2,Y1               ;y1 = y2
        MOVE      Z2,Z1               ;z1 = z2
        MOVE      OC2,OC1             ;outcode1 = outcode2
        DSJ       COUNT,YLOOP         ;loop if more vertices
        JRUC      YBACK               ;otherwise, return

*-----------------------------------------------------------------------
* Clip edge from vertex 1 to vertex 2 to top or bottom bounding plane.
*-----------------------------------------------------------------------
Y1OUT:
        MOVE      Y1,Y1               ;is y1 negative? (out bottom?)
        JRNN      Y11                 ;jump if y1 >= 0 (out top)
; Calculate value of independent parameter t to clip to bottom plane.
        MOVE      Z1,T1               ;
        ADD       Y1,T1               ;v = z1 + y1
        MOVE      T1,U0               ;
        SUB       Z2,U0               ;
        SUB       Y2,U0               ;u = (z1 + y1) - (z2 + y2)
        JRUC      Y12                 ;
; Calculate value of independent parameter t to clip to top plane.
Y11:
        MOVE      Z1,T1               ;
        SUB       Y1,T1               ;v = z1 - y1
        MOVE      T1,U0               ;
        SUB       Z2,U0               ;
        ADD       Y2,U0               ;u = (z1 - y1) - (z2 - y2)
Y12:
        MOVE      T1,T0               ;
        SRA       16,T0               ;calculate (v << 16) to 64 bits
        SLL       16,T1               ;
        DIVS      U0,T0               ;t = (v << 16) / u (range 0..1)
; Use parameter t to clip x1 to top or bottom plane.
        MOVE      X2,U0               ;
        SUB       X1,U0               ;
        MPYS      T0,U0               ;t * (x2 - x1)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      X1,U1               ;x1 ' = x1 + (t * (x2-x1) >> 16)
        MOVE      U1,*POUT+,0         ;store x1' in temp. output buffer
; Clip y1 to top or bottom plane.
        MOVE      Y2,U0               ;
        SUB       Y1,U0               ;
        MPYS      T0,U0               ;t * (y2 - y1)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      Y1,U1               ;y1 ' = y1 + (t * (y2-y1) >> 16)
        MOVE      U1,*POUT+,0         ;store y1' in temp. output buffer
; Clip z1 to top or bottom plane.
        ABS       U1                  ;z1' = |y1'|
        MOVE      U1,*POUT+,0         ;store z1' in temp. output buffer
; Increment count of output vertices.
        INC       NVERTS              ;
        JRUC      YL3                 ;

*-----------------------------------------------------------------------
* Clip edge from vertex 2 to vertex 1 to top or bottom bounding plane.
*-----------------------------------------------------------------------
Y2OUT:
        MOVE      Y2,Y2               ;is y2 < 0 ? (out bottom?)
        JRNN      Y21                 ;jump if y1 >= 0 (out top plane)
; Calculate value of independent parameter t to clip to bottom plane.
        MOVE      Z2,T1               ;
        ADD       Y2,T1               ;v = z2 + y2
        MOVE      T1,U0               ;
        SUB       Z1,U0               ;
        SUB       Y1,U0               ;u = (z2 + y2) - (z1 + y1)
        JRUC      Y22                 ;
; Calculate value of independent parameter t to clip to top plane.
Y21:
        MOVE      Z2,T1               ;
        SUB       Y2,T1               ;v = z2 - y2
        MOVE      T1,U0               ;
        SUB       Z1,U0               ;
        ADD       Y1,U0               ;u = (z2 - y2) - (z1 - y1)
Y22:
        MOVE      T1,T0               ;
        SRA       16,T0               ;calculate (v << 16) to 64 bits
        SLL       16,T1               ;
        DIVS      U0,T0               ;t = (v<<16) / u (16-bit fraction)
; Use parameter t to clip x2 to top or bottom bounding plane.
        MOVE      X1,U0               ;
        SUB       X2,U0               ;
        MPYS      T0,U0               ;t * (x1 - x2)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      X2,U1               ;x2 ' = x2 + (t * (x1-x2) >> 16)
        MOVE      U1,*POUT+,0         ;store x2' in temp. output buffer
; Clip y2 to top or bottom bounding plane.
        MOVE      Y1,U0               ;
        SUB       Y2,U0               ;
        MPYS      T0,U0               ;t * (y1 - y2)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      Y2,U1               ;y2 ' = y2 + (t * (y1-y2) >> 16)
        MOVE      U1,*POUT+,0         ;store y2' in temp. output buffer
; Clip z2 to top or bottom bounding plane.
        ABS       U1                  ;z2' = |y2'|
        MOVE      U1,*POUT+,0         ;store z2' in temp. output buffer
        JRUC      YL4                 ;


*-----------------------------------------------------------------------
*
*               Clip to left and right bounding planes.
*
*-----------------------------------------------------------------------
XCLIP:

        CLR       OC1                 ;initialize outcode1 = 0
; Read back x1,y1,z1 from end of previous output list.
        MOVE      -*POUT,Z1,0         ;read z1
        MOVE      -*POUT,Y1,0         ;read y1
        MOVE      -*POUT,X1,0         ;read x1
; Jump here if [x1 y1 z1 z1] are still valid from before.
XCLIP1:
        MOVE      BUFB,POUT           ;set up pointer to output buffer
        NEG       Z1                  ;replace z1 with -z1
; Compute outcode for vertex 1 based on whether x1 violates clip planes.
        CMP       Z1,X1               ;is -z1 > x1 ? (out left?)
        JRGE      XC1                 ;jump if -z1 <= x1 (inside left)
        MOVK      1,OC1               ;set outcode1 = 1
XC1:
        NEG       Z1                  ;restore positive sign: +z2
        CMP       Z1,X1               ;is +z1 < x1 ? (outside right?)
        JRLE      XLOOP               ;jump if +z1 >= x1 (inside right)
        MOVK      2,OC1               ;set outcode1 = 2

*-----------------------------------------------------------------------
* Each iteration below clips (if necessary) one polygon edge.
*-----------------------------------------------------------------------
XLOOP:
        MOVE      *PIN+,X2,0          ;get x2 from input list
        MOVE      *PIN+,Y2,0          ;get y2 from input list
        MOVE      *PIN+,Z2,0          ;get z2 from input list
        NEG       Z2                  ;get -z2
; Compute outcode for vertex 2 based on whether y2 violates clip planes.
        CLR       OC2                 ;initialize outcode2 = 0
        CMP       Z2,X2               ;is -z2 > x2 ? (outside w+y=0 plane?)
        JRGE      XL1                 ;jump if -z2 <= x2 (inside)
        MOVK      1,OC2               ;set outcode2 = 1
XL1:
        NEG       Z2                  ;get +z2
        CMP       Z2,X2               ;is +z2 < x2 ? (outside w-y=0 plane?)
        JRLE      XL2                 ;jump if +z2 >= x2 (inside)
        MOVK      2,OC2               ;set outcode2 = 2
XL2:
        MOVE      OC1,BTEMP           ;copy outcode1
        AND       OC2,BTEMP           ;outcode1 & outcode2
        JRNZ      XL5                 ;jump if both vertices out on same side
; Is vertex 1 outside left or right clipping plane?  If so, clip.
        MOVE      OC1,OC1             ;is vertex 1 outside top/bottom plane?
        JRNZ      X1OUT               ;jump if vertex 1 outside plane
; Is vertex 2 outside left or right clipping plane?  If so, clip.
XL3:
        MOVE      OC2,OC2             ;is vertex 2 outside top/bottom plane?
        JRNZ      X2OUT               ;jump if vertex 2 outside plane
; Coordinate x2 is inside both the left and right clipping planes.
        MOVE      X2,*POUT+,0         ;store x2
        MOVE      Y2,*POUT+,0         ;store y2
        MOVE      Z2,*POUT+,0         ;store z2
; Increment count of output vertices.
XL4:
        INC       NVERTS              ;
; Jump here if vertices 1 and 2 both outside the same clipping plane.
XL5:
        MOVE      X2,X1               ;x1 = x2
        MOVE      Y2,Y1               ;y1 = y2
        MOVE      Z2,Z1               ;z1 = z2
        MOVE      OC2,OC1             ;outcode1 = outcode2
        DSJ       COUNT,XLOOP         ;loop if more vertices
        JRUC      XBACK               ;otherwise, return

*-----------------------------------------------------------------------
* Clip edge from vertex 1 to vertex 2 to left or right bounding plane.
*-----------------------------------------------------------------------
X1OUT:
        MOVE      X1,X1               ;is x1 negative? (left plane?)
        JRNN      X11                 ;jump if x1 >= 0 (right plane)
; Calculate value of independent parameter t to clip to left plane.
        MOVE      Z1,T1               ;
        ADD       X1,T1               ;v = z1 + x1
        MOVE      T1,U0               ;
        SUB       Z2,U0               ;
        SUB       X2,U0               ;u = (z1 + x1) - (z2 + x2)
        JRUC      X12                 ;
; Calculate value of independent parameter t to clip to right plane.
X11:
        MOVE      Z1,T1               ;
        SUB       X1,T1               ;v = z1 - x1
        MOVE      T1,U0               ;
        SUB       Z2,U0               ;
        ADD       X2,U0               ;u = (z1 - x1) - (z2 - x2)
X12:
        MOVE      T1,T0               ;
        SRA       16,T0               ;calculate (v << 16) to 64 bits
        SLL       16,T1               ;
        DIVS      U0,T0               ;t = (v << 16) / u (range 0..1)
; Use parameter t to clip x1 to left or right plane.
        MOVE      X2,U0               ;
        SUB       X1,U0               ;
        MPYS      T0,U0               ;t * (x2 - x1)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      X1,U1               ;x1 ' = x1 + (t * (x2-x1) >> 16)
        MOVE      U1,*POUT+,0         ;store x1' in temp. output buffer
        MOVE      U1,T1               ;save x1'
; Clip y1 to left or right plane.
        MOVE      Y2,U0               ;
        SUB       Y1,U0               ;
        MPYS      T0,U0               ;t * (y2 - y1)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      Y1,U1               ;y1 ' = y1 + (t * (y2-y1) >> 16)
        MOVE      U1,*POUT+,0         ;store y1' in temp. output buffer
; Clip z1 to left or right plane.
        ABS       T1                  ;z1' = |x1'|
        MOVE      T1,*POUT+,0         ;store z1' in temp. output buffer
; Increment count of output vertices.
        INC       NVERTS              ;
        JRUC      XL3                 ;

*-----------------------------------------------------------------------
* Clip edge from vertex 2 to vertex 1 to left or right bounding plane.
*-----------------------------------------------------------------------
X2OUT:
        MOVE      X2,X2               ;is x2 < 0 ? (out left?)
        JRNN      X21                 ;jump if x1 >= 0 (out right)
; Calculate value of independent parameter t to clip to left plane.
        MOVE      Z2,T1               ;
        ADD       X2,T1               ;v = z2 + x2
        MOVE      T1,U0               ;
        SUB       Z1,U0               ;
        SUB       X1,U0               ;u = (z2 + x2) - (z1 + x1)
        JRUC      X22                 ;
; Calculate value of independent parameter t to clip to right plane.
X21:
        MOVE      Z2,T1               ;
        SUB       X2,T1               ;v = z2 - x2
        MOVE      T1,U0               ;
        SUB       Z1,U0               ;
        ADD       X1,U0               ;u = (z2 - x2) - (z1 - x1)
X22:
        MOVE      T1,T0               ;
        SRA       16,T0               ;calculate (v << 16) to 64 bits
        SLL       16,T1               ;
        DIVS      U0,T0               ;t = (v<<16) / u (16-bit fraction)
; Use parameter t to clip x2 to left or right bounding plane.
        MOVE      X1,U0               ;
        SUB       X2,U0               ;
        MPYS      T0,U0               ;t * (x1 - x2)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      X2,U1               ;x2 ' = x2 + (t * (x1-x2) >> 16)
        MOVE      U1,*POUT+,0         ;store x2' in temp. output buffer
        MOVE      U1,T1               ;save x2'
; Clip y2 to left or right bounding plane.
        MOVE      Y1,U0               ;
        SUB       Y2,U0               ;
        MPYS      T0,U0               ;t * (y1 - y2)
        MOVX      U0,U1               ;concatenate 2 halves of result
        RL        16,U1               ;undo shift left of v by 16
        ADDC      Y2,U1               ;y2 ' = y2 + (t * (y1-y2) >> 16)
        MOVE      U1,*POUT+,0         ;store y2' in temp. output buffer
; Clip z2 to left or right bounding plane.
        ABS       T1                  ;z2' = |x2'|
        MOVE      T1,*POUT+,0         ;store z2' in temp. output buffer
        JRUC      XL4                 ;

        .end

