; *****************************************************************************
; *****************************************************************************
;
;                               Draw Sprites
;
; *****************************************************************************
; *****************************************************************************

; *****************************************************************************
;
;         Draw Sprite. Sprite Data ^ BX, Scanline Range in CL..CH
;
; *****************************************************************************

_ScanLo:db      0                       ; Start & End displayed scanlines.
_ScanHi:db      0
_SLCount:db     0                       ; scanline counter
_SLCtrl:db      0                       ; control byte (from byte 2 of sprites)
_BaseAddr:dw    0                       ; base address of sprites
_SVector:dw     0                       ; sprite drawing routine

DrawSprite:
        push    ax,bx,cx

        mov     al,[bx]                 ; check range of sprite coords
        cmp     al,0                    ; y must be > 0 and <= 240 to draw
        jz      _DSExit2
        cmp     al,240
        jbe     _DSDraw
_DSExit2:
        jmp     _DSExit

_DSDraw:
        mov     [_ScanLo],cl            ; save scan line boundaries
        mov     [_ScanHi],ch

        mov     al,1[bx]                ; get pattern # in ax
        xor     ah,ah
        shl     ax,4                    ; ax = pattern# * 16
        test    byte [PPUCtrl1],8       ; adjust if using other pattern bank
        jz      _DS1                    ; for sprites
        add     ax,01000h
_DS1:
        mov     [_BaseAddr],ax          ; save the base address
        add     ax,VROM                 ; now points into VROM
        mov     di,ax                   ; di points to the sprite patterns now.

        mov     al,2[bx]                ; get the attribute bits
        and     al,3
        shl     al,2                    ; x by 4
        xor     ah,ah
        mov     si,NESPalette+16        ; si point to base.
        add     si,ax

        mov     cl,[si]                 ; get the colours.
        mov     ch,1[si]
        mov     dl,2[si]
        mov     dh,3[si]
        and     cx,3F3Fh
        add     cx,4040h
        and     dx,3F3Fh
        add     dx,4040h

        mov     al,2[bx]                ; get the control bit
        mov     [_SLCtrl],al

        mov     ah,[bx]                 ; ah = y coordinate
        mov     al,3[bx]                ; al = x coordinate

        ;
        ;       now cl/ch/dl/dh contain the colours, (al,ah) position
        ;       and si points to the bit data
        ;

        mov     es,[VSSegment]
        mov     byte [_SlCount],8       ; number of write lines
        mov     word [_SVector],SpriteBitLine

        test    byte [PPUCtrl1],32      ; 8 x 16 sprites ?
        jz      _DS2
        mov     byte [_SlCount],16

_DS2:   test    byte [_SLCtrl],32       ; behind background
        jz      _DS4
        mov     word [_SVector],SpriteBehindLine
_DS4:
        test    byte [_SLCtrl],128      ; vertical flip
        jz      DoLine
        add     di,7                    ; start from the bottom.
      
DoLine: call    SPGetLine               ; get the screen line address into bx
        cmp     bx,0                    ; if zero,don't draw on this line
        jz      DoNextLine
        cmp     ah,[_ScanLo]            ; if < Lo Scan,don't draw it.
        jb      DoNextLine
        cmp     ah,[_ScanHi]            ; if > Hi Scan,finish this sprite
        jae     _DSExit

        push    ax                      ; save coordinates
        xor     ah,ah                   ; ax = x position
        add     bx,ax                   ; bx = where the sprite goes
        mov     al,[di]                 ; get the bit patterns out.
        mov     ah,8[di]

        test    byte [_SLCtrl],64       ; x flipping ????
        jz      NoXFlip
        call    XReverse
NoXFlip:

        push    ds                      ; draw the bitmap onto the screen

        push    es
        pop     ds
        call    [cs:_SVector]
        pop     ds,ax                   ; restore coordinates

DoNextLine:
        inc     ah                      ; next vertical scan line
        inc     di                      ; next byte from pattern table
        test    byte [_SLCtrl],128      ; vertically flipped ???
        jz      _DS3
        dec     di                      ; prev byte from pattern table
        dec     di
_DS3:                                   ; if its an 8x16 sprite
        cmp     byte [_SlCount],9       ; change the pattern table
        jne     _DS5                    ; to the other one
        mov     di,[_BaseAddr]
        xor     di,16
        add     di,VROM
_DS5:
        dec     byte [_SlCount]
        jnz     DoLine

_DSExit:
        pop     cx,bx,ax
        ret

SPGetLine:                              ; get video line in bx for line ah
#if LineSize=320
        push    cx
        mov     bl,ah                   ; bl = line #
        xor     bh,bh
        add     bx,bx                   ; bx = line # * 2
        add     bx,[LineTable]
        mov     cx,[bx]                 ; get the line address
        mov     bx,cx
        pop     cx
        ret
#else
        mov     bh,ah
        xor     bl,bl
        sub     bh,8
        ret
#endif

XReverse:                               ; reverse al & ah
        push    bx
        mov     bl,al
        xor     bh,bh
        mov     al,Reverse[bx]
        mov     bl,ah
        mov     ah,Reverse[bx]
        pop     bx
        ret
;
;       this is the same as Bitline, except it uses the SprBlastVec
;       table rather than the BitBlastVec table
;
;       the main difference is that the 0 graphic isn't written out.
;

SpriteBitLine:                          ; here ah = h7-h4 h3-h0 al = l7-l4 l3-l0
        ror     al,4                    ; now  ah = h7-h4 h3-h0 al = l3-l0 l7-l4
        ror     ax,4                    ; now  ah = l7-l4 h7-h4 al = h3-h0 l3-l0
        ror     ah,4                    ; now  ah = h7-h4 l7-l4 al = h3-h0 l3-l0

        push    ax                      ; save al for later,we're doing ah first
        mov     al,ah                   ; si = 0 al
        xor     ah,ah
        add     ax,ax
        mov     si,ax
        call    SprBlastVec[cs:si]      ; call the writing vector
        add     bx,4                    ; next nibl

        pop     ax
        xor     ah,ah
        add     ax,ax
        mov     si,ax
        call    SprBlastVec[cs:si]
        add     bx,LineSize-4           ; next line down
        ret

SpriteBehindLine:
        push    cx,dx
        mov     cl,8                    ; bits to write counter
SBL1:   mov     ch,[bx]                 ; look at the screen
        and     ch,63
        cmp     ch,cs:[NESPalette]
        jne     SBL2                    ; try the next one.

        test    ah,128
        jnz     SBLBit1Set
        test    al,128                  ; ah & 128 = 0 now
        jz      SBL2
        mov     ch,1[cs:si]
        jmp     SBLW
SBLBit1Set:
        test    al,128                  ; ah & 128 = 1 now
        mov     ch,2[cs:si]
        jz      SBLW
        mov     ch,3[cs:si]

SBLW:   and     ch,63
        add     ch,64
        mov     [bx],ch

SBL2:   shl     ax,1                    ; shift ah & al right one
        inc     bx                      ; next pixel
        dec     cl
        jnz     SBL1

        add     bx,LineSize-8
        pop     dx,cx
        ret
