        .width    132
        .title    'display list interpreter'

*----------------------------------------------------------------------
*                                    TIGA
*          Copyright (C) 1989-1990  Texas Instruments Incorporated.
*                            All Rights Reserved
*----------------------------------------------------------------------
* displist_color function
*
*   The displist_color function is a display list interpreter.  It is
*   similar to the displist_mono function, but draws on color rather
*   than monochrome displays.  The displist_color function can interpret
*   a display list specifying a series of polygons and dots to be drawn
*   to the screen.  The specification for each polygon or dot begins on
*   an even 32-bit boundary in memory, and the first item in each
*   specification is a function code indicating the type of graphics
*   primitive to be drawn.  The 4-bit color code for each polygon is
*   interpreted by this function as an index into a 16-entry color
*   lookup table.
*
*   A polygon is specified in the display list by a function code of 0.
*   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 1.  The display list format specifying a dot is as
*   follows:
*
*             8-bit function code:     0
*             8-bit constant:          0
*             16-bit dot 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 display list is terminated by a function code of -1.
*----------------------------------------------------------------------
*  Usage:  displist_color(p);
*
*  Description of stack arguments:
*    short *p;        /* pointer to start of display list */
*
*  Returned in register A8:  undefined
*
*  Registers altered:  A8
*----------------------------------------------------------------------
* Revision history:
*   03/21/89...Original version written..................Jerry Van Aken
*----------------------------------------------------------------------
;
;
;  DECLARE GLOBAL FUNCTION NAME
;
        .globl    _displist_color

;
;
;   DEFINE CONSTANTS
;
YLINE   .set      A0                  ;y value at current scan line
NVERTS  .set      A0                  ;number of vertices in polygon
ATEMP   .set      A1                  ;A-file temporary register
X1REAL  .set      A2                  ;fixed-pt x1 at left side of fill
DX1     .set      A3                  ;change in x1 per unit step in y
X2REAL  .set      A4                  ;fixed-pt x2 at right side of fill
DX2     .set      A5                  ;change in x2 per unit step in y
XY1END  .set      A6                  ;x,y at end of edge on left side
XY2END  .set      A7                  ;x,y at end of edge on right side
H1      .set      A8                  ;height of edge on left side
H2      .set      A9                  ;height of edge on right side
P1      .set      A10                 ;pointer to next vertex on left side
P2      .set      A11                 ;pointer to next vertex on right side
P       .set      A12                 ;pointer to bottom of ptlist array
XYSTART .set      A14                 ;starting vertex of current edge
STK     .set      A14                 ;C parameter stack pointer
XYORG   .set      B0                  ;viewport xy origin displacement
COUNT   .set      B1                  ;loop counter
DADDR   .set      B2                  ;destination xy address for fills
DYDX    .set      B7                  ;width and height of fill area
COLOR1  .set      B9                  ;fill color
BTEMP   .set      B14                 ;B-file temporary register
;
;
;   ENTRY POINT
;
_displist_color:

        SETF      32,0,0              ;set up for 32-bit moves
; Save registers.
        MMTM      SP,A0,A1,A2,A3,A4,A5,A6,A7,A9,A10,A11,A12,A13
        MMTM      SP,B0,B1,B2,B9,B10,B11,B12,B13,B14
; Pop argument from program stack.
        MOVE      -*STK,P,0           ;get pointer p

        .align

; Interpret next function code.
NEXT:
        SETF      8,1,1
        MOVE      *P+,ATEMP,1         ;get next module address
        JRZ       DOT                 ;if f.c. = 1, draw dots
        JRNN      POLYGON             ;if f.c. = 0, draw polygon


*----------------------------------------------------------------------
*   Display list interpreter module:  exit display list interpreter
*----------------------------------------------------------------------
EXIT:

        MMFM      SP,B0,B1,B2,B9,B10,B11,B12,B13,B14
        MMFM      SP,A0,A1,A2,A3,A4,A5,A6,A7,A9,A10,A11,A12,A13
        SETF      32,0,1              ;restore default field size 1
        MOVE      *SP(32),STK,1       ;update program stack pointer
        RETS      2                   ;


*----------------------------------------------------------------------
*        Display list interpreter module:  draw convex polygon
*----------------------------------------------------------------------
POLYGON:

        MOVE      *P+,P1,1            ;get vertex count
        SETF      16,0,1              ;set up for 16-bit moves
        MOVE      *P+,ATEMP,1         ;get polygon color
        MOVE      ATEMP,COLOR1        ;set fill color register
        MOVX      COLOR1,BTEMP        ;copy 16 LSBs of COLOR1
        SLL       16,BTEMP            ;
        MOVY      BTEMP,COLOR1        ;replicate in 16 MSBs of COLOR1
        MOVE      P,P2                ;copy pointer to start of xy array
        SLL       5,P1                ;convert count to 32-bit offset
        ADD       P2,P1               ;add base address of xy array
        MOVE      P1,P                ;update display list pointer
        MOVE      *P2+,XY1END,0       ;get xy coord's at topmost vertex
        MOVE      XY1END,XY2END       ;left, right sides begin at top
; Find y value along pixel centers of topmost scan line to be filled.
        MOVE      XY1END,YLINE        ;copy initial y value
        SRA       16,YLINE            ;convert to FIX2 format
        ADDK      1,YLINE             ;round up by 1/2 - 1/4
        SRA       2,YLINE             ;truncate to integer
; Set fill height = 1 (polygon drawn as series of horizontal lines).
        MOVK      1,DYDX              ;
        SLL       16,DYDX             ;
; Initialize heights of edge segments on left and right sides.
        CLR       H1                  ;left edge height = 0
        CLR       H2                  ;right edge height = 0

; Main Loop:  Each iteration fills a trapezoidal region of the polygon.
; Calculate y value along pixel centers of next scan line to be filled.
LOOP1:
        SETF      4,1,1               ;set up for 4-bit multiplies
        SLL       2,YLINE             ;convert yline to FIX2
        ADDK      2,YLINE             ;y at pixel centers = yline + 1/2
        SLL       16,YLINE            ;shift yline+1/2 into 16 MSBs

; Have we reached the next vertex on the left side of the polygon?
        MOVE      H1,H1               ;check y distance to next vertex
        JRNZ      NOTYET1             ;jump if not yet at next vertex
; Arrived at starting vertex for next edge.  Need to get ending vertex.
; Update pointer to point to next vertex (the ending vertex) in list.
WHILE1:
        CMP       P2,P1               ;compare pointers
        JRLT      NEXT                ;done if pointers pass each other
        MOVE      XY1END,XYSTART      ;save start vertex coord's (xs,ys)
        MOVE      -*P1,XY1END,0       ;get end vertex coord's (xe,ye)
; If we have finally reached the bottom of the polygon, we are done.
        MOVE      XY1END,ATEMP        ;
        SUBXY     XYSTART,ATEMP       ;a = xe-xs, b = ye-ys
;---?   JRYN      NEXT                ;done if ye < ys (reached bottom)
; If edge is too short to reach to next scan line, go to next edge.
        MOVE      XY1END,H1           ;make copy of end vertex
        SUBXY     YLINE,H1            ;subtract y of next scan line
        JRYN      WHILE1              ;too short--go to next edge
        SRL       16+2,H1             ;truncate to integer
        INC       H1                  ;no. scan lines covered by edge
; Get edge's inverse slope:  deltax = change in x per unit step in y.
        MOVE      ATEMP,DX1           ;copy width a of edge
        SLL       16,DX1              ;a << 16
        SRL       16,ATEMP            ;right justify b (is positive)
        DIVS      ATEMP,DX1           ;deltax = (a << 16) / b
; Calculate initial fixed-point x where edge meets next scan line.
        MOVE      YLINE,X1REAL        ;
        SUBXY     XYSTART,X1REAL      ;(yline + 1/2 - ys) in 16 MSBs
        SRL       16,X1REAL           ;right justify fractional part
        MOVE      DX1,ATEMP           ;copy deltax
        MPYS      X1REAL,ATEMP        ;(yline + 1/2 - ys) * deltax
        MOVE      XYSTART,X1REAL      ;
        SLL       16,X1REAL           ;left justify starting x value
        ADD       ATEMP,X1REAL        ;adjust starting x value
        SRA       2,X1REAL            ;integer part of x in 16 MSBs
        ADDI      07FFFh,X1REAL       ;add in rounding constant
NOTYET1:

; Have we reached the next vertex on the right side of the polygon?
        MOVE      H2,H2               ;check y distance to next vertex
        JRNZ      NOTYET2             ;jump if not yet at next vertex
; Arrived at starting vertex for next edge.  Need to get ending vertex.
; Update pointer to point to next vertex (the ending vertex) in list.
WHILE2:
        CMP       P2,P1               ;compare pointers
        JRLT      NEXT                ;done if pointers pass each other
        MOVE      XY2END,XYSTART      ;save start vertex coord's (xs,ys)
        MOVE      *P2+,XY2END,0       ;get end vertex coord's (xe,ye)
; If we have finally reached the bottom of the polygon, we are done.
        MOVE      XY2END,ATEMP        ;
        SUBXY     XYSTART,ATEMP       ;a = xe-xs, b = ye-ys
;---?   JRYN      NEXT                ;done if ye < ys (reached bottom)
; If edge is too short to reach to next scan line, go to next edge.
        MOVE      XY2END,H2           ;make copy of end vertex
        SUBXY     YLINE,H2            ;subtract y of next scan line
        JRYN      WHILE2              ;too short--go to next edge
        SRL       16+2,H2             ;truncate to integer
        INC       H2                  ;no. scan lines covered by edge
; Get edge's inverse slope:  deltax = change in x per unit step in y.
        MOVE      ATEMP,DX2           ;copy width a of edge
        SLL       16,DX2              ;a << 16
        SRL       16,ATEMP            ;right justify b (is positive)
        DIVS      ATEMP,DX2           ;deltax = (a << 16) / b
; Calculate initial fixed-point x where edge meets next scan line.
        MOVE      YLINE,X2REAL        ;
        SUBXY     XYSTART,X2REAL      ;(yline + 1/2 - ys) in 16 MSBs
        SRL       16,X2REAL           ;right justify fractional part
        MOVE      DX2,ATEMP           ;copy deltax
        MPYS      X2REAL,ATEMP        ;(yline + 1/2 - ys) * deltax
        MOVE      XYSTART,X2REAL      ;
        SLL       16,X2REAL           ;left justify starting x value
        ADD       ATEMP,X2REAL        ;adjust starting x value
        SRA       2,X2REAL            ;integer part of x in 16 MSBs
        ADDI      07FFFh,X2REAL       ;add in rounding constant
NOTYET2:

*------------------------------------------------------------------------------
; Determine height of next trapezoidal area to be filled.
        MOVE      H1,ATEMP            ;copy height of left edge segment
        CMP       H1,H2               ;compare with right edge height
        JRNN      OKAY                ;jump if left edge shorter
        MOVE      H2,ATEMP            ;right edge is shorter
OKAY:
        SUB       ATEMP,H1            ;h1 -= count
        SUB       ATEMP,H2            ;h2 -= count
        MOVE      ATEMP,COUNT         ;initial loop count = trap. height
        SRA       16+2,YLINE          ;right justify yline integer part

*------------------------------------------------------------------------------
; Inner loop:  Fill trapezoidal area of specified height.
TFILL:
        MOVE      X2REAL,ATEMP        ;copy x coord. along right side
        SUBXY     X1REAL,ATEMP        ;fill width = int(x2) - int(x1)
        JRC       NEXT                ;jump if width < 0
        SRL       16,ATEMP            ;move width into 16 LSBs
        MOVE      ATEMP,BTEMP         ;copy to B-file
        MOVX      BTEMP,DYDX          ;move width into 16 LSBs of DYDX
        MOVY      X1REAL,YLINE        ;concat. int(x1) with scan line y
        MOVE      YLINE,DADDR         ;copy to dest'n address register
        RL        16,DADDR            ;move x1 to LSBs, yline to MSBs
        FILL      XY                  ;fill one horizontal line segment
        ADD       DX1,X1REAL          ;increment fixed-point x1
        ADD       DX2,X2REAL          ;increment fixed-point x2
        INC       YLINE               ;increment integer scan line y
        DSJ       COUNT,TFILL         ;loop 'til bottom of trapezoid
; Done filling current trapezoidal region of polygon.  Are there more?
        JRUC      LOOP1               ;go get next trapezoid to fill


*----------------------------------------------------------------------
*             Display list interpreter module:  draw a dot
*----------------------------------------------------------------------
DOT:

        ADDK      8,P                 ;increment pointer
        SETF      16,0,1              ;set up for 16-bit data moves
        MOVE      *P+,ATEMP,1         ;get dot color
        MOVE      *P+,P1,0            ;get xy coord's of next dot
        PIXT      ATEMP,*P1.XY        ;draw the dot
        JRUC      NEXT                ;interpret next function code

        .end

