**
**  I2C communication with VLab hardware
**  İ 2002 Kakace Productions
**
**

                INCDIR  Devkit:NDK_3.9/Include_ASM
                INCLUDE hardware/custom.i

                TTL     "FAR"
                MC68020


*--------------------------------------------------------------------------------------------------*
*========================================== I2C CONTROL ===========================================*
*--------------------------------------------------------------------------------------------------*
;
; The VLab Classic and VLab Y/C use the same implementation.
;

SAA9051_ADDRESS         EQU     $8A     ; I²C address
I2C_WRITE               EQU     $00     ; Write mode
I2C_READ                EQU     $01     ; Read mode

REG_I2C_CONTROL         EQU     $0901   ; I2C bus control register.
REG_I2C_READ            EQU     $0B01


                XDEF    _VLab_WriteRegisters
                XDEF    _VLab_ReadStatus
                XREF    _custom


******* I2C_Protocol/VLab_WriteRegisters ************************************
*
*   NAME
*   VLab_WriteRegisters -- Write to SAA9051 registers
*
*   SYNOPSIS
*   failure = VLab_WriteRegisters(VLabAddress, stream, lentgh)
*
*   UBYTE VLab_WriteRegisters(void *, UBYTE *, ULONG);
*
*   FUNCTION
*   Write to SAA9051 registers through the I²C bus. The data stream must
*   follows I²C communication rules where the first byte is the chip's ID
*   and the second byte is the offset of the first register to write to.
*   Remaining bytes are the values to be stored into successive registers
*   starting from the specified one.
*
*   INPUTS
*   VLabAddress - I/O address of the VLab frame grabber.
*   stream      - Pointer to the data stream.
*   length      - Length (in bytes) of the data stream.
*
*   RESULT
*   failure - TRUE if an error occured (bit 0 set).
*
*   SEE ALSO
*
*****************************************************************************
*
*


_VLab_WriteRegisters
                movea.l 4(sp),a1                ; param := VLabAddress
                movea.l 8(sp),a0                ; param := stream
                move.l  12(sp),d0               ; param := length

                moveq   #0,d1                   ; Don't read anything
                bra.s   RW_Registers            ; d0 = Success/Failure indicator (bit 0)


******* I2C_Protocol/VLab_ReadStatus ****************************************
*
*   NAME
*   VLab_ReadStatus -- Read status byte from SAA9051
*
*   SYNOPSIS
*   status = VLab_ReadStatus(VLabAddress)
*
*   UBYTE VLab_ReadStatus(void *);
*
*   FUNCTION
*
*   INPUTS
*   VLabAddress - I/O address of the VLab frame grabber.
*
*   RESULT
*   status - Status byte returned by SAA9051 through the I²C bus. Bit #0,
*            which is not used by the SAA9051, is set by this function to
*            reflect an I²C communication error.
*
*   SEE ALSO
*
*****************************************************************************
*
*


_VLab_ReadStatus
                movea.l 4(sp),a1                ; VLabAddress

                moveq   #1,d0                   ; Send one byte
                moveq   #-1,d1                  ; Read the status
                lea     .status_stream(pc),a0
                bra.s   RW_Registers            ; d0 = Status byte (bit 0 = success/failure)


.status_stream  dc.b    SAA9051_ADDRESS|I2C_READ,$00


*****i* I2C_Protocol/RW_Registers *******************************************
*
*   NAME
*   RW_Registers -- Read/Write to SAA9051's registers (ASM only)
*
*   SYNOPSIS
*   status = RW_Registers(stream, length, VLabAddress, readFlag)
*                           a0       d1       a1          d1
*
*   UBYTE RW_Registers(UBYTE *, ULONG, void *, LONG);
*
*   FUNCTION
*   Write the datas pointed to by stream to SAA9051's registers, then read
*   the status byte.
*
*   INPUTS
*   stream      - Pointer to the data stream, including chip ID and base
*                 address (offset) of the first register to write to.
*   length      - Length (in bytes) of the data stream.
*   VLabAddress - I/O address of the VLab frame grabber.
*   readFlag    - TRUE if we shall read the status byte, FALSE otherwise.
*
*   SEE ALSO
*
*****************************************************************************
*
* I²C lines :
*
* START :  SDA  H ŻŻ\                    STOP :  SDA  H       /ŻŻŻ    ;  If SDA changes while SCL
*               L    \_____                           L _____/        ;  is high, this is a START
*                                                                     ;  or a STOP condition.
*          SCL  H ŻŻŻŻŻ\                         SCL  H    /ŻŻŻŻŻŻ    ;
*               L       \___                          L __/           ;
*
*
*
*          bit          0      1      2      3      4      5      6      7      ACK
* WRITE :  SDA  H ŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻŻŻ\/ŻŻŻ
*               L __/\_____/\_____/\_____/\_____/\_____/\_____/\_____/\_____/         \___
*
*          SCL  H      /Ż\    /Ż\    /Ż\    /Ż\    /Ż\    /Ż\    /Ż\    /Ż\     /Ż\
*               L ____/   \__/   \__/   \__/   \__/   \__/   \__/   \__/   \___/   \___
*
*
*          bit          0      1      2      3      4      5      6      7      ACK
* READ :   SDA  H ŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/ŻŻŻŻŻ\/\     /\/ŻŻŻ
*               L __/\_____/\_____/\_____/\_____/\_____/\_____/\_____/\_____/  \___/  \___
*
*          SCL  H      /Ż\    /Ż\    /Ż\    /Ż\    /Ż\    /Ż\    /Ż\    /Ż\     /Ż\
*               L ____/   \__/   \__/   \__/   \__/   \__/   \__/   \__/   \___/   \___
*
* ACK after reading : The master pull SDA low then issues a SCL pulse, then release the SDA line.


SCL_LO          MACRO                   ; reg
                bclr    #1,\1
                bsr     Zorro_I2C
                ENDM

SCL_HI          MACRO                   ; reg
                bset    #1,\1
                bsr     Zorro_I2C
                ENDM

SDA_LO          MACRO                   ; reg
                bclr    #0,\1
                bsr     Zorro_I2C
                ENDM

SDA_HI          MACRO                   ; reg
                bset    #0,\1
                bsr     Zorro_I2C
                ENDM


RW_Registers    movem.l d2-d6,-(sp)
                moveq   #0,d4                   ; SCL/DTA bits
                move.w  d0,d5
                move.w  d1,d6
                SDA_HI  d4                      ; Release the DTA line.
                                                ; If CLK is high, this send a STOP.

                SCL_HI  d4                      ; Send a START
                SDA_LO  d4

                SCL_LO  d4                      ; Drive the clock line
                bra.s   .write                  ; then begins writes

        ; Write the data stream, byte by byte

.writeByte      moveq   #7,d2
                move.b  (a0)+,d3

.writeBit       add.b   d3,d3
                bcc.s   .bit_0

                SDA_HI  d4
                bra.s   .nextBit

.bit_0          SDA_LO  d4

.nextBit        SCL_HI  d4                      ; Clock pulse
                SCL_LO  d4
                dbra    d2,.writeBit

                SDA_HI  d4                      ; Release the DATA line for handshake
                move.w  #$1FFF,d2               ; Timeout value (arbitrary)

.ACK_loop       SCL_HI  d4
                dbeq    d2,.ACK_loop            ; Loop until SDA low

                SCL_LO  d4                      ; Drive the clock line.
                moveq   #0,d3
                tst.w   d2
                bmi.s   .timeOut                ; Not acknowledged : exit

.write          dbra    d5,.writeByte
                tst.w   d6
                beq.s   .releaseBus

        ; Read the status byte

.readByte       moveq   #7,d2

.readBit        SCL_HI  d4                      ; CLOCK Hi to read a bit
                sne     d0
                addq.b  #1,d0
                addx.b  d3,d3
                SCL_LO  d4                      ; then CLOCK low (pulse)
                dbra    d2,.readBit

                bclr    #0,d3
                bra.s   .releaseBus

.timeOut        bset    #0,d3                   ; Failure

.releaseBus     SDA_LO  d4                      ; Drive DTA line
                SCL_HI  d4                      ; then send a STOP
                SDA_HI  d4
                move.l  d3,d0
                movem.l (sp)+,d2-d6
                rts


*****i* I2C_Protocol/Zorro_I2C **********************************************
*
*   NAME
*   Zorro_I2C -- CLK/DTA driving routine (ASM only)
*
*   SYNOPSIS
*   DTA_in = Zorro_I2C(VLabAddress, serialBits)
*                          d1           d4
*
*   bit Zorro_I2C(void *, UBYTE);
*
*   FUNCTION
*   Drive CLK/DTA lines.
*
*   INPUTS
*   VLabAddress - I/O address of the VLab frame grabber.
*   serialBits  - New level of DTA/CLK lines.
*
*   RESULT
*   CCR[z] - Z bit reflects the status of the DTA line.
*
*   NOTES
*   The function uses a busy loop to slow down I²C transfert rate, which is
*   limited to 100 kbits/s. Each read to the vposr register requires ~0.7 µs
*   so the loop runs for 10 µs.
*
*   SEE ALSO
*
*****************************************************************************
*
*


Zorro_I2C       move.b  d4,REG_I2C_CONTROL(a1)

                moveq   #15,d0
.busy_Loop      tst.w   _custom+vposr
                dbra    d0,.busy_Loop

                btst    #0,REG_I2C_READ(a1)
                rts


