; > TestSrc.Cmos

        TTL RISC OS 2+ POST battery-backed RAM access
;
; A function to read bytes from CMOS, for use in verifying the checksum
; and reading memory test flag & video modes.
;------------------------------------------------------------------------
; History
;
; Date          Name            Comment
; ----          ----            -------
; 05-Apr-91     ArtG            Initial version, based on IICMod.
;
;
;------------------------------------------------------------------------
;
; in:
;       R0 = device address          (bit 8 - 15   register address    )
;       R1 = length of block to read
;       R2 = initial sum value
;
; out:  R0 = sum of all bytes in block
;       R1 - R13 trashed
;  

ts_CMOSread     ROUT

        MOV     R13,R14
        MOV     R8,R2                   ; initialise accumulator
        MOV     R7,R1                   ; initialise byte counter
        MOV     R6,R0                   ; stash register address
        MOV     R2, #IOC
        MOV     R0, #-1                 ; ensure timer is ticking
        STRB    R0, [R2, #Timer0LL]     ; (nonzero in input latch)
        STRB    R0, [R2, #Timer0LH]
        STRB    R0, [R2, #Timer0GO]     ; load the count registers
        BL      ts_Start
        BEQ     %FT30                   ; check clock line toggles OK
        AND     R0, R6, #&FE
        BL      ts_TXCheckAck           ; transmit device address (write)
        BVS     %FT30
        MOV     R0, R6, LSR #8
        BL      ts_TXCheckAck           ; write register address
        BVS     %FT30
        BL      ts_Start                ; Extra START bit to switch modes
        AND     R0, R6, #&FE
        ORR     R0, R0, #1
        BL      ts_TXCheckAck           ; transmit device address (read)
        BVS     %FT30
20
        BL      ts_RXByte               ; read byte from bus
        ADD     R8, R8, R0              ; accumulate total
        SUBS    R7, R7, #1              ; is it last byte ?
        MOVNE   R0, #0                  ; no, then acknowledge with 0 bit
        MOVEQ   R0, #1                  ; yes, then don't acknowledge
        BL      ts_ClockData            ; but always send ack clock pulse
        TEQ     R7, #0                  ; loop, until last byte
        BNE     %BT20
30
        MOVVS   R7, #-1                 ; pass error indicator to caller
        BL      ts_Stop
        MOV     R0, R8
        TEQ     R7, #0                  ; return zero flag if read OK
        MOV     PC,R13

; *****************************************************************************
;
;       TXCheckACK - transmit a byte and wait for slave to ACK
;
;  out: Trashes r0,r1,r2,r3,r4,r5,r9,r10,r11,r12
;       V bit set on error.
;

ts_TXCheckAck ROUT
        MOV     R12,R14
        BL      ts_TXByte
        BL      ts_Acknowledge
        MOVVC   PC, R12                 ; acknowledged ok, so return
        ORRS    PC, R12, #V_bit

; *****************************************************************************
;
;       SetC1C0 - Set clock and data lines to values in R1 and R0 respectively
;
; out:  Trashes r0,r1,r2,r11
;

ts_SetC1C0 ROUT
        MOV     R11, R14
        BIC     R14, R14, #Z_bit                ; indicate not checking clock
ts_SetOrCheck
        ORR     R14, R14, #I_bit                ; disable interrupts
        TEQP    R14, #0

        ADD     R0, R0, R1, LSL #1              ; R0 := C0 + C1*2

        ORR     R0, R0, #&C0                    ; make sure two test bits are
                                                ; always set to 1 !
        MOV     R2, #IOC
        STRB    R0, [R2, #IOCControl]
10
        LDREQB  R1, [R2, #IOCControl]           ; wait for clock
        TSTEQ   R1, #i2c_clock_bit              ; to read high
        BEQ     %BT10

        MOV     R0, #10                         ; delay for >= 10/2 microsecs
;
; in-line do-micro-delay to save a stack level
;
        STRB    R0, [R2, #Timer0LR]     ; copy counter into output latch
        LDRB    R1, [R2, #Timer0CL]     ; R1 := low output latch
20
        STRB    R0, [R2, #Timer0LR]     ; copy counter into output latch
        LDRB    R14, [R2, #Timer0CL]    ; R14 := low output latch
        TEQ     R14, R1                 ; unchanged ?
        MOVNE   R1, R14                 ; copy anyway
        BEQ     %BT20                   ; then loop
        SUBS    R0, R0, #1              ; decrement count
        BNE     %BT20                   ; loop if not finished
;
; end do-micro-delay
;
        MOV     PC, R11

; Set clock and data lines to R1 and R0 and then wait for clock to be high

ts_SetC1C0CheckClock ROUT
        MOV     R11, R14
        ORR     R14, R14, #Z_bit                ; indicate checking clock
        B       ts_SetOrCheck


; *****************************************************************************
;
;       ClockData - Clock a bit of data down the IIC bus
;
; in:   R0 = data bit
;
; out:  Trashes r0,r1,r2,r3,r10,r11
;

ts_ClockData ROUT
        MOV     R10,R14

        MOV     R3, R0                  ; save data
        MOV     R1, #0                  ; clock LO
        BL      ts_SetC1C0

        MOV     R1, #1                  ; clock HI
        MOV     R0, R3
        BL      ts_SetC1C0CheckClock

; Delay here must be >= 4.0 microsecs

        MOV     R1, #0                  ; clock LO
        MOV     R0, R3
        BL      ts_SetC1C0

        MOV     PC,R10

; *****************************************************************************
;
;       Start - Send the Start signal
;
; out:  Trashes r0,r1,r2,r9,r11
;       R0 (and Z flag) indicates state of clock .. should be NZ.
;

ts_Start   ROUT
        MOV     R9,R14

        MOV     R0, #1                  ; clock HI, data HI
        MOV     R1, #1
        BL      ts_SetC1C0

; Delay here must be >= 4.0 microsecs

        MOV     R0, #0                  ; clock HI, data LO
        MOV     R1, #1
        BL      ts_SetC1C0

; Make sure clock really is high (and not shorted to gnd)

        LDRB    R3, [R2, #IOCControl]

; Delay here must be >= 4.7 microsecs

        MOV     R0, #0                  ; clock LO, data LO
        MOV     R1, #0
        BL      ts_SetC1C0

        ANDS    R0, R3, #i2c_clock_bit
        MOV     PC,R9

; *****************************************************************************
;
;       Acknowledge - Check acknowledge after transmitting a byte
;
; out:  Trashes r0,r1,r2,r3,r9,r11
;       V=0 => acknowledge received
;       V=1 => no acknowledge received
;

ts_Acknowledge ROUT
        MOV     R9,R14

        MOV     R0, #1                  ; clock LO, data HI
        MOV     R1, #0
        BL      ts_SetC1C0

        MOV     R0, #1                  ; clock HI, data HI
        MOV     R1, #1
        BL      ts_SetC1C0CheckClock

; Delay here must be >= 4.0 microsecs

        MOV     R2, #IOC
        LDRB    R3, [R2, #IOCControl]   ; get the data from IOC

        MOV     R0, #1                  ; clock LO, data HI
        MOV     R1, #0
        BL      ts_SetC1C0

        TST     R3, #1                  ; should be LO for correct acknowledge
        MOV     R3, PC
        BICEQ   R3, R3, #V_bit          ; clear V if correct acknowledge
        ORRNE   R3, R3, #V_bit          ; set V if no acknowledge
        TEQP    R3, #0

        MOV     PC,R9

; *****************************************************************************
;
;       Stop - Send the Stop signal
;
; out:  Trashes r0,r1,r2,r9,r11
;

ts_Stop    ROUT
        MOV     R9,R14

        MOV     R0, #0                  ; clock HI, data LO
        MOV     R1, #1
        BL      ts_SetC1C0

; Delay here must be >= 4.0 microsecs

        MOV     R0, #1                  ; clock HI, data HI
        MOV     R1, #1
        BL      ts_SetC1C0

        MOV     PC,R9

; *****************************************************************************
;
;       TXByte - Transmit a byte
;
; in:   R0 = byte to be transmitted
;
; out:  Trashes r0,r1,r2,r3,r4,r5,r9,r10,r11
;

ts_TXByte  ROUT
        MOV     R9, R14
        MOV     R4, R0                  ; byte goes into R4
        MOV     R5, #&80                ; 2^7   the bit mask
10
        ANDS    R0, R4, R5              ; zero if bit is zero
        MOVNE   R0, #1
        BL      ts_ClockData            ; send the bit
        MOVS    R5, R5, LSR #1
        BNE     %BT10
        MOV     PC, R9

; *****************************************************************************
;
;       RXByte - Receive a byte
;
; out:  R0 = byte received
;       Trashes r1,r2,r3,r4,r9,r11
;

ts_RXByte  ROUT
        MOV     R9, R14
        MOV     R3, #0                  ; byte:=0
        MOV     R2, #IOC
        MOV     R4, #7

        MOV     R0, #1                  ; clock LO, data HI
        MOV     R1, #0
        BL      ts_SetC1C0
10
        MOV     R0, #1                  ; pulse clock HI
        MOV     R1, #1
        BL      ts_SetC1C0CheckClock

        LDRB    R1, [R2, #IOCControl]   ; get the data from IOC
        AND     R1, R1, #1
        ADD     R3, R1, R3, LSL #1      ; byte:=byte*2+(IOC?0)AND1

        MOV     R0, #1                  ; return clock LO
        MOV     R1, #0
        BL      ts_SetC1C0

        SUBS    R4, R4, #1
        BCS     %BT10

        MOV     R0, R3                  ; return the result in R0  
        MOV     PC, R9

        LTORG

        END