**
**  Hand made and optimized VLab Y/C Zorro II driver
**  © 2002 Kakace Productions
**
**


                INCDIR  Devkit:NDK_3.9/Include_ASM

                TTL     "FAR"
                MC68020


*--------------------------------------------------------------------------------------------------*
*========================== V L A B - M O N I T O R I N G - D R I V E R ===========================*
*--------------------------------------------------------------------------------------------------*

                INCLUDE intuition/intuition.i
                INCLUDE hardware/custom.i


; VLab Y/C pseudo-registers

REGISTER_0080   EQU     $0080   ; Read before scanning/reading datas
REGISTER_0101   EQU     $0101   ; Start scan control register ?
REGISTER_0200   EQU     $0200   ; Read before scanning

REG_YC_RESETCNT EQU     $0A80
REG_YC_SYNCBITS EQU     $0B01   ; Sync bits
REG_YC_READ_Y   EQU     $0800   ; Y port
REG_YC_READ_UV  EQU     $0981   ; U,V port
REG_READ16      EQU     $3C40   ; Skip 16 Y values at once


*--------------------------------------------------------------------------------------------------*

                XDEF    _VLabYC_SkipLuma
                XDEF    _VLabYC_SkipChroma
                XDEF    _VLabYC_GrabLuma
                XDEF    _VLabYC_GrabChroma

                XREF    _IntuitionBase
                XREF    _VLab_Y_Correction
                XREF    UV_Table


MIX_Y                   EQU     1               ; 1 to mix luma values, 0 otherwise.

CAPTURE_HIRES           EQU     0
CAPTURE_LORES           EQU     1
CAPTURE_THUMBNAIL       EQU     2


******* VLab_YC/VLabYC_SkipLuma *********************************************
*
*   NAME
*   VLabYC_SkipLuma -- Skip the top of frame
*   VLabYC_skipLuma -- Skip luma values (ASM only)
*
*   SYNOPSIS
*   VLabYC_SkipLuma(VLabAddress, field, pixels)
*   VLabYC_skipLuma(VLabAddress, pixels)
*                       a0         d0
*
*   VOID VLabYC_SkipLuma(void *, ULONG, ULONG);
*   VOID VLabYC_skipLuma(void *, ULONG);
*
*   FUNCTION
*   Skip luma values (top of frame). If the first field is selected, this
*   also causes a new scan to be made.
*   The VLabYC_skipLuma function only skips Y values.
*
*   INPUTS
*   VLabAddress - I/O address of the VLab frame grabber.
*   field       - Field number (1 or 2).
*   pixels      - How many Y pixels to skip (unit = 720 pixels/line)
*
*   NOTES
*   The scanning function polls hardware registers until the new frame has
*   been scanned.
*
*   SEE ALSO
*
*****************************************************************************
*
*


_VLabYC_SkipLuma
                movea.l 4(sp),a0                ; VLab address
                move.l  8(sp),d0                ; Field number

                subq.l  #1,d0
                bne.s   .secondField

        ; First field : Scan the picture.

                tst.w   REGISTER_0080(a0)
                tst.w   REGISTER_0200(a0)
                clr.b   REGISTER_0101(a0)       ; Trigger a new grab ?
                move.b  #1,REGISTER_0101(a0)

                movea.l _IntuitionBase,a1
                move.l  ib_Seconds(a1),d0
                addq.l  #2,d0                   ; Timeout limit.

.wait1          cmp.l   ib_Seconds(a1),d0
                bcs.s   .timeout

                btst    #1,REG_YC_SYNCBITS(a0)
                beq.s   .wait1                  ; Mean loop count : 15000

.wait2          btst    #1,REG_YC_SYNCBITS(a0)
                bne.s   .wait2                  ; Almost never loop back

.timeout        tst.w   REGISTER_0080(a0)
                tst.w   REG_YC_RESETCNT(a0)

        ; We'll skip 4 pixels at once. The VLab Y/C hardware
        ; can also skips 16 pixels at once.

.secondField    move.l  12(sp),d0               ; Pixel count.

VLabYC_skipLuma                                 ; Internal entry point to skip margins.
                moveq   #3,d1
                lsr.l   #2,d0
                and.l   d0,d1                   ; How many 4-pixel blocks to skip.
                lsr.l   #2,d0                   ; How many 16-pixel blocks to skip.

                subq.w  #1,d0                   ; max = 720×310/16 = 13950
                bcs.s   .skip4pix

.fast_skip      tst.w   REG_READ16(a0)          ; Skip 16 pixels.
                dbra    d0,.fast_skip

.skip4pix       subq.w  #1,d1
                bcs.s   .exit

.slow_skip      tst.l   REG_YC_READ_Y(a0)
                dbra    d1,.slow_skip

.exit           rts


******* VLab_YC/VLabYC_SkipChroma *******************************************
*
*   NAME
*   VLabYC_SkipChroma -- Skip the top of frame
*   VLabYC_skipChroma -- Skip luma values (ASM only)
*
*   SYNOPSIS
*   VLabYC_SkipChroma(VLabAddress, field, pixels)
*   VLabYC_skipChroma(VLabAddress, pixels)
*                       a0         d0
*
*   VOID VLabYC_SkipChroma(void *, ULONG, ULONG);
*   VOID VLabYC_skipChroma(void *, ULONG);
*
*   FUNCTION
*   Skip chroma values (top of frame). The VLabYC_skipLuma function only
*   skips U and V values.
*
*   INPUTS
*   VLabAddress - I/O address of the VLab frame grabber.
*   field       - Field number (1 or 2).
*   pixels      - How many pixels to skip (unit = 720 pixels/line). Note
*                 that this value is the number of luma components, NOT the
*                 number of U,V pairs !
*
*   SEE ALSO
*
*****************************************************************************
*
*


_VLabYC_SkipChroma
                movea.l 4(sp),a0                ; VLab address
                move.l  8(sp),d0                ; Field number
                subq.l  #1,d0
                bne.s   .secondField

                tst.w   REG_YC_RESETCNT(a0)     ; Reset the internal address

.secondField    move.l  12(sp),d0               ; Pixel count (Y pixels)

VLabYC_skipChroma                               ; Internal entry point to skip margins.
                lsr.l   #2,d0                   ; Apply the YUV ratio.
                subq.l  #1,d0                   ; Max = 720 * 310 / 4 = 55800
                bcs.s   .exit

.skip           tst.b   REG_YC_READ_UV(a0)      ; U and V values are multiplexed
                tst.b   REG_YC_READ_UV(a0)      ; into 2 bytes.
                dbra    d0,.skip

.exit           rts


******* VLab_YC/VLabYC_GrabLuma *********************************************
*
*   NAME
*   VLabYC_GrabLuma -- Fills a buffer with Y (luma) values.
*
*   SYNOPSIS
*   VLabYC_GrabLuma(VLabAddress, output, output_spacing, width, height,
*               line_skip, mode)
*
*   VOID VLabYC_GrabLuma(void *, UBYTE *, ULONG, ULONG, ULONG, ULONG, ULONG);
*
*   FUNCTION
*   Translates then stores Y (luma) values to the buffer pointed to by
*   output.
*
*   INPUTS
*   VLabAddress    - I/O address of the VLab frame grabber.
*   output         - Pointer to the output buffer.
*   output_spacing - How many bytes to skip between each line when writing
*                    to the output buffer. This is used for lace scans where
*                    frame fields are interleaved.
*   width          - Image width (number of pixels)
*   height         - Image height (number of lines per field).
*   line_skip      - How many pixels to skip from the last pixel of a line
*                    to the first pixel of the next line.
*   mode           - Resolution ID (CAPTURE_#?). The mode determines the
*                    output format (4:1:1, 2:1:1 or 1:1:1).
*
*   NOTES
*   lSkip is expressed in hardware unit (720 pixels per scanline).
*
*   SEE ALSO
*
*****************************************************************************
*
*


_VLabYC_GrabLuma
                movem.l d2-d7/a2,-(sp)                  ; 7 registers

ARGBASE         SET     8*4

                movea.l ARGBASE(sp),a0                  ; VLab address
                movea.l ARGBASE+4(sp),a1                ; output buffer
                lea     _VLab_Y_Correction,a2           ; Y correction table.

                move.l  ARGBASE+24(sp),d0               ; mode
                move.l  ARGBASE+16(sp),d6               ; height
                move.l  ARGBASE+20(sp),d4               ; line_skip (Y pixels)

                subq.l  #CAPTURE_LORES,d0
                beq     lumaYC_lores
                bhi     lumaYC_thumbnail

        ;-----------------------------------------------
        ; x_scale = 1 : Use all Y values.
        ;-----------------------------------------------

lumaYC_hires
.encode_line    move.l  ARGBASE+12(sp),d5               ; width

                * a0 : Board's address          d4 : Line skip counter
                * a1 : Output buffer            d5 : Width (blocks of 4 pixels)
                * a2 : Y correction table       d6 : Height (output lines)
                *                               d7 : Output format

.encode_pixel   move.l  REG_YC_READ_Y(a0),d2            ; Yn     Yn+1   Yn+2   Yn+3
                move.l  #$00FF00FF,d3

                and.l   d2,d3                           ;  0     Yn+1    0     Yn+3
                eor.l   d3,d2                           ; Yn      0     Yn+2    0
                lsr.l   #8,d2                           ;  0     Yn      0     Yn+2

                move.b  (a2,d3.w),d3                    ;  0     Yn+1    0     Y'n+3
                move.b  (a2,d2.w),d2                    ;  0     Yn      0     Y'n+2
                swap    d3
                swap    d2
                move.b  (a2,d3.w),d3                    ;  0     Y'n+3   0     Y'n+1
                move.b  (a2,d2.w),d2                    ;  0     Y'n+2   0     Y'n

                ror.l   #8,d2                           ; Y'n    0      Y'n+2   0
                swap    d3                              ;  0     Y'n+1   0     Y'n+3
                or.l    d3,d2                           ; Y'n    Y'n+1  Y'n+2  Y'n+3

                move.l  d2,(a1)+
                subq.l  #4,d5
                bne.s   .encode_pixel

                subq.l  #1,d6
                bne.s   .skip_margins

                movem.l (sp)+,d2-d7/a2
                rts

                ; Skip margins and blank lines.

.skip_margins   add.l   ARGBASE+8(sp),a1                ; output += output_spacing
                move.l  d4,d0                           ; line_skip
                bsr     VLabYC_skipLuma
                bra.s   .encode_line


        ;-----------------------------------------------
        ; x_scale = 2 : Discard one Y value in each pair
        ;-----------------------------------------------

lumaYC_lores
.encode_line    move.l  ARGBASE+12(sp),d5               ; width

                * a0 : Board's address          d4 : Line skip counter
                * a1 : Output buffer            d5 : Width (blocks of 4 pixels)
                * a2 : Y correction table       d6 : Height (output lines)
                *                               d7 : Output format

.encode_pixel   move.l  REG_YC_READ_Y(a0),d2            ; Yn     Yn+1   Yn+2   Yn+3

        IFNE    MIX_Y
                move.l  #$00FF00FF,d1
                and.l   d2,d1                           ;  0     Yn+1    0     Yn+3
                eor.l   d1,d2                           ; Yn      0     Yn+2    0
                lsr.l   #8,d2                           ;  0     Yn      0     Yn+2

                add.l   d1,d2                           ; Mix : (Yn + Yn+1)/2  (Yn+2 + Yn+3)/2
                addi.l  #$00010001,d2
                lsr.l   #1,d2
                and.w   #$00FF,d2                       ; Remove bit #15
        ELSE
                andi.l  #$00FF00FF,d2                   ;  0     Yn+1    0     Yn+3
        ENDC

                swap    d2
                move.b  (a2,d2.w),d1                    ; Y'n
                swap    d2
                asl.w   #8,d1
                move.b  (a2,d2.w),d1                    ; Y'n  Y'n+1

                move.w d1,(a1)+

                subq.l  #2,d5
                bne.s   .encode_pixel

                subq.l  #1,d6
                bne.s   .skip_margins

                movem.l (sp)+,d2-d7/a2
                rts

                ; Skip margins and blank lines.

.skip_margins   add.l   ARGBASE+8(sp),a1                ; output += output_spacing
                move.l  d4,d0                           ; line_skip
                bsr     VLabYC_skipLuma
                bra.s   .encode_line


        ;-----------------------------------------------
        ; x_scale = 1 : Discard 3 Y values in each quadruple
        ;-----------------------------------------------

lumaYC_thumbnail
.encode_line    move.l  ARGBASE+12(sp),d5               ; width

                * a0 : Board's address          d4 : Line skip counter
                * a1 : Output buffer            d5 : Width (blocks of 4 pixels)
                * a2 : Y correction table       d6 : Height (output lines)
                *                               d7 : Output format

.encode_pixel   move.l  REG_YC_READ_Y(a0),d2            ; Yn     Yn+1   Yn+2   Yn+3

        IFNE    MIX_Y
                move.l  #$00FF00FF,d1
                and.l   d2,d1                           ;  0     Yn+1    0     Yn+3
                eor.l   d1,d2                           ; Yn      0     Yn+2    0
                lsr.l   #8,d2                           ;  0     Yn      0     Yn+2
                add.l   d2,d1

                move.w  d1,d2                           ;  0    [Yn+2 + Yn+3]
                swap    d1                              ;  0    [Yn   + Yn+1]
                add.w   d1,d2
                addq.w  #2,d2
                lsr.w   #2,d2                           ; = (Yn + Yn+1 + Yn+2 + Yn+3) / 4
        ELSE
                swap    d2                              ; Yn     Yn+1
        ENDC

                andi.w  #$00FF,d2                       ;  0     Yn+1
                move.b  (a2,d2.w),(a1)+

                subq.l  #1,d5
                bne.s   .encode_pixel

                subq.l  #1,d6
                bne.s   .skip_margins

                movem.l (sp)+,d2-d7/a2
                rts

                ; Skip margins and blank lines.

.skip_margins   add.l   ARGBASE+8(sp),a1                ; output += output_spacing
                move.l  d4,d0                           ; line_skip
                bsr     VLabYC_skipLuma
                bra.s   .encode_line


******* VLab_YC/VLabYC_GrabChroma *******************************************
*
*   NAME
*   VLabYC_GrabChroma -- Fills a buffer with U,V (chroma) pairs.
*
*   SYNOPSIS
*   VLabYC_GrabChroma(VLabAddress, output, output_spacing, width, height,
*               line_skip)
*
*   VOID VLabYC_GrabChroma(void *, UBYTE *, ULONG, ULONG, ULONG, ULONG);
*
*   FUNCTION
*   Translates then stores U,V (chroma) pairs to the buffer pointed to by
*   output.
*
*   INPUTS
*   VLabAddress    - I/O address of the VLab frame grabber.
*   output         - Pointer to the output buffer.
*   output_spacing - How many bytes to skip between each line when writing
*                    to the output buffer. This is used for lace scans where
*                    frame fields are interleaved.
*   width          - Image width (number of pixels, hardware units)
*   height         - Image height (number of lines per field).
*   line_skip      - How many pixels to skip from the last pixel of a line
*                    to the first pixel of the next line.
*
*   NOTES
*   line_skip and width are expressed in hardware unit (720 pixels per
*   scanline).
*
*   SEE ALSO
*
*****************************************************************************
*
*


_VLabYC_GrabChroma
                movem.l d2-d5/a2,-(sp)                  ; 5 registers

ARGBASE         SET     6*4

                movea.l ARGBASE(sp),a0                  ; VLab address
                movea.l ARGBASE+4(sp),a1                ; output
                lea     UV_Table,a2                     ; U/V correction.
                move.l  ARGBASE+16(sp),d5               ; Height

.encode_line    move.l  ARGBASE+12(sp),d4               ; width
                lsr.l   #2,d4                           ; YUV ratio
                subq.l  #1,d4

.encode_pixel   move.b  REG_YC_READ_UV(a0),d0
                move.b  REG_YC_READ_UV(a0),d1

                moveq   #$33,d2
                moveq   #$33,d3
                andi.w  #$FA,d1                         ; v := [u5 u6  v5 v6  u7 --  v7 --]

                and.b   d0,d2                           ; [ 0  0  v1 v2   0  0  v3 v4]
                and.b   d1,d3                           ; [ 0  0  v5 v6   0  0  v7 --]
                eor.b   d2,d0                           ; [u1 u2   0  0  u3 u4   0  0]
                eor.b   d3,d1                           ; [u5 u6   0  0  u7 --   0  0]
                lsr.b   #2,d1                           ; [ 0  0  u5 u6   0  0  u7 --]
                lsl.b   #2,d2                           ; [v1 v2   0  0  v3 v4   0  0]
                or.b    d0,d1                           ; [u1 u2  u5 u6  u3 u4  u7 --]
                or.b    d3,d2                           ; [v1 v2  v5 v6  v3 v4  v7 --]

                ; Join U and V to reorder bits 2-5

                swap    d2
                move.w  d1,d2                           ; [v1 v2  v5 v6  v3 v4  v7 --]  [u1 u2  u5 u6  u3 u4  u7 --]

                move.l  d2,d1
                lsr.l   #2,d1
                eor.l   d2,d1
                andi.l  #$000C000C,d1
                eor.l   d1,d2
                lsl.l   #2,d1
                eor.l   d1,d2

                move.b  (a2,d2.w),(a1)+                 ; U'
                swap    d2
                move.b  1(a2,d2.w),(a1)+                ; V'

.next_pixel     dbra    d4,.encode_pixel

                subq.l  #1,d5
                beq.s   .GrabChromaExit                 ; Don't skip margins after the last line.

                ; Skip margins and blank lines.

                adda.l  ARGBASE+8(sp),a1                ; output += output_spacing
                move.l  ARGBASE+20(sp),d0               ; line_skip
                bsr     VLabYC_skipChroma
                bra.s   .encode_line

.GrabChromaExit movem.l (sp)+,d2-d5/a2
                rts


