**
**  Hand made and optimized VLab Classic Zorro II driver
**  © 2002 Kakace Productions
**
*******************************************************************************
*******************   I M P O R T A N T   N O T I C E   ***********************
*******************************************************************************
**
**  Although the VLab Classic and VLab Y/C are quite similar, the underlying
**  hardware is not the same. For instance, Y and UV ports are not the same.
**
**  Most of the code below is derived from the working VLab Y/C driver,
**  modified to address VLab Classic specific ports. Those parts should work
**  as expected, but this remains to be proved.
**
**  The VLab_Capture function is the more probable culprit if this driver
**  doesn't work. Although it has been derived from the original code used in
**  the vlab.library, the internals are very specific compared to the VLab Y/C
**  scanning function. Therefore, I assume the code is broken until someone
**  proves that it works.
**
*******************************************************************************

                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
                INCLUDE exec/ables.i


; VLab pseudo-registers (Some registers differ depending on the VLab model)

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_CL_RESETCNT EQU     $0280
REG_CL_SYNCBITS EQU     $0301
REG_CL_READ_Y   EQU     $0000
REG_CL_READ_UV  EQU     $0401


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

                XDEF    _VLab_SkipLuma
                XDEF    _VLab_SkipChroma
                XDEF    _VLab_GrabLuma
                XDEF    _VLab_GrabChroma

                XREF    _IntuitionBase
                XREF    _SysBase
                XREF    _VLab_Y_Correction
                XREF    _intena
                XREF    _custom
                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_Classic/VLab_SkipLuma ******************************************
*
*   NAME
*   VLab_SkipLuma -- Skip the top of frame
*   VLab_skipLuma -- Skip luma values (ASM only)
*
*   SYNOPSIS
*   VLab_SkipLuma(VLabAddress, field, pixels)
*   VLab_skipLuma(VLabAddress, pixels)
*                       a0         d0
*
*   VOID VLab_SkipLuma(void *, ULONG, ULONG);
*   VOID VLab_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
*
*****************************************************************************
*
*


_VLab_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.

                bsr.b   VLab_Capture
                tst.w   REG_CL_RESETCNT(a0)

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

VLab_skipLuma                                   ; Internal entry point to skip margins;
                lsr.l   #2,d0
                subq.w  #1,d0
                bcs.s   .exit

.slow_skip      tst.l   REG_CL_READ_Y(a0)
                dbra    d0,.slow_skip

.exit           rts


*****i* VLab_Classic/VLab_Capture *******************************************
*
*   NAME
*   VLab_Capture -- Scan a new image (ASM only)
*
*   SYNOPSIS
*   VLab_Capture(VLabAddress, fieldID)
*                    a0          d0
*
*   VOID VLab_Capture(void *, ULONG);
*
*   FUNCTION
*   Capture a new picture.
*
*   INPUTS
*   VLabAddress - I/O address of the VLab frame grabber.
*   FieldID     - Field number (1 or 2).
*
*   NOTES
*   This looks like the driver handles the scanning operation by monitoring
*   sync signals. Perhaps something like the VLab Y/C driver could be done.
*
*   BUGS
*   This function is merely derived from the original code (vlab.library).
*   SINCE I DON'T HAVE A VLAB CLASSIC AT HAND, THIS MATERIAL HAS NOT BEEN
*   TESTED.
*
*   SEE ALSO
*
*****************************************************************************
*
*


VLab_Capture    move.l  d2,-(sp)
                move.l  d0,d1                   ; Field number
                moveq   #2,d2
                movea.l _SysBase,a1
                DISABLE a1

.scanLoop       move.b  #1,REGISTER_0101(a0)

                moveq   #-1,d0
.sync_1         btst    #0,REG_CL_SYNCBITS(a0)
                dbeq    d0,.sync_1
                bne.s   .scan

                moveq   #-1,d0
.sync_2         btst    #0,REG_CL_SYNCBITS(a0)
                dbne    d0,.sync_2
                beq.s   .scan

                moveq   #-1,d0
.sync_3         btst    #0,REG_CL_SYNCBITS(a0)
                dbeq    d0,.sync_3
                bne.s   .scan

                btst    #1,REG_CL_SYNCBITS(a0)
                beq.s   .sync_1

.sync_4         btst    #1,REG_CL_SYNCBITS(a0)
                bne.s   .sync_4

                tst.w   REGISTER_0200(a0)

                moveq   #-1,d0
.sync_5         btst    #0,REG_CL_SYNCBITS(a0)
                dbeq    d0,.sync_5
                bne.s   .scan

                moveq   #-1,d0
.sync_6         btst    #0,REG_CL_SYNCBITS(a0)
                dbne    d0,.sync_6
                beq.s   .scan

                tst.b   d1                      ; Single frame
                beq.s   .scan

                moveq   #-1,d0
.sync_7         btst    #0,REG_CL_SYNCBITS(a0)
                dbeq    d0,.sync_7
                bne.s   .scan

                moveq   #-1,d0
.sync_8         btst    #0,REG_CL_SYNCBITS(a0)
                dbne    d0,.sync_8

.scan           move.b  #0,REGISTER_0101(a0)
                tst.w   REGISTER_0080(a0)
                tst.w   REGISTER_0200(a0)

                moveq   #15,d0
.busy_Loop      tst.w   _custom+vposr           ; 10 µs delay
                dbra    d0,.busy_Loop

                tst.w   REGISTER_0080(a0)
                tst.w   REG_CL_RESETCNT(a0)

                move.w  #(720/4)-1,d0
.readLine       tst.l   REG_CL_READ_Y(a0)
                dbra    d0,.readLine

                move.w  REG_CL_READ_Y(a0),d0
                btst    #0,d0
                beq.s   .retry

                btst    #8,d0
                bne.s   .exit

.retry          dbra    d2,.scanLoop

.exit           ENABLE  a1
                move.l  (sp)+,d2
                rts


******* VLab_Classic/VLab_SkipChroma ****************************************
*
*   NAME
*   VLab_SkipChroma -- Skip the top of frame
*   VLab_skipChroma -- Skip luma values (ASM only)
*
*   SYNOPSIS
*   VLab_SkipChroma(VLabAddress, field, pixels)
*   VLab_skipChroma(VLabAddress, pixels)
*                       a0         d0
*
*   VOID VLab_SkipChroma(void *, ULONG, ULONG);
*   VOID VLab_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
*
*****************************************************************************
*
*


_VLab_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_CL_RESETCNT(a0)     ; Reset the internal address

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

VLab_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_CL_READ_UV(a0)      ; U and V values are multiplexed
                tst.b   REG_CL_READ_UV(a0)      ; into 2 bytes.
                dbra    d0,.skip

.exit           rts



******* VLab_Classic/VLab_GrabLuma ******************************************
*
*   NAME
*   VLab_GrabLuma -- Fills a buffer with Y (luma) values.
*
*   SYNOPSIS
*   VLab_GrabLuma(VLabAddress, output, output_spacing, width, height,
*               line_skip, mode)
*
*   VOID VLab_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
*
*****************************************************************************
*
*


_VLab_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     luma_lores
                bhi     luma_thumbnail

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

luma_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_CL_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     VLab_skipLuma
                bra.s   .encode_line


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

luma_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_CL_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     VLab_skipLuma
                bra.s   .encode_line


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

luma_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_CL_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     VLab_skipLuma
                bra.s   .encode_line


******* VLab_Classic/VLab_GrabChroma ****************************************
*
*   NAME
*   VLab_GrabChroma -- Fills a buffer with U,V (chroma) pairs.
*
*   SYNOPSIS
*   VLab_GrabChroma(VLabAddress, output, output_spacing, width, height,
*               line_skip)
*
*   VOID VLab_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
*
*****************************************************************************
*
*


_VLab_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_CL_READ_UV(a0),d0
                move.b  REG_CL_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     VLab_skipChroma
                bra.s   .encode_line

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


