;* ----------------------------------------------------------------------------
;*  Copyright (C) 2004 Dallas Semiconductor Corporation, All Rights Reserved.
;*
;*  Permission is hereby granted, free of charge, to any person obtaining a
;*  copy of this software and associated documentation files (the "Software"),
;*  to deal in the Software without restriction, including without limitation
;*  the rights to use, copy, modify, merge, publish, distribute, sublicense,
;*  and/or sell copies of the Software, and to permit persons to whom the
;*  Software is furnished to do so, subject to the following conditions:
;*
;*  The above copyright notice and this permission notice shall be included
;*  in all copies or substantial portions of the Software.
;*
;*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
;*  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;*  MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
;*  IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
;*  OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
;*  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
;*  OTHER DEALINGS IN THE SOFTWARE.
;*
;*  Except as contained in this notice, the name of Dallas Semiconductor
;*  shall not be used except as stated in the Dallas Semiconductor
;*  Branding Policy.
;*
;*  Module:        maxq2000_ow
;*  Description:   Utility and API functions for using the MAXQ2000 1-Wire bus.
;*  Filename:      maxq2000_ow.h
;*  Date:          November 2, 2004
;*  Version:       1.00
;*
;* ----------------------------------------------------------------------------

#include "maxQ2000.inc"

#define OW_COMMAND          0
#define OW_TXRX_BUFFER      1
#define OW_INTERRUPT_FLAGS  2
#define OW_INTERRUPT_ENABLE 3
#define OW_CLOCK_DIVISOR    4
#define OW_CONTROL          5
  .code
;*******************************************************************************
;*
;* Function:    ow_configureclock
;*
;* Configures the 1-Wire Clock Control register to have the appropriate values
;* for the prescaler bits and the divider bits.
;*
;* Input:       ACC - the value of the microcontroller's clock in MHz.
;*
;* Output:      ACC - zero if the function was successful.
;*
;* Destroys:    GR, A[0], A[1], AP = 0, PSF, OWA, OWD
;*
;*******************************************************************************
ow_configureclock::

    move    A[1], ACC                ; Save off input value.
    move    AP, #0                   ; Set A[0] to be active ACC.

    move    ACC, #25                 ; Make sure input is < 26.
    sub     A[1]
    jump    C, ow_configureclock_done

    move    ACC, A[1]                ; Make sure input is > 3.
    sub     #4
    jump    C, ow_configureclock_done

    sla                              ; Use the difference as an index into
    add     #ow_clocktable           ;  our clock value table.
    move    GR, ACC                  ; Save off the pointer.

    move    OWA, #OW_CLOCK_DIVISOR   ; Activate the CLOCK DIVISOR register.
    move    ACC, OWD                 ; Read the current value.
    and     #0E0h                    ; Mask off the bits we are going to set.
    call    GR                       ; Call into the table to set the new value.
    move    OWD, ACC                 ; Write the new value.

    move    ACC, #0                  ; Clear ACC to indicate success.
ow_configureclock_done:
    ret

ow_clocktable:
    or      #88h     ; 4
    ret
    or      #82h     ; 5
    ret
    or      #85h     ; 6
    ret
    or      #83h     ; 7
    ret
    or      #8Ch     ; 8
    ret
    or      #8Ch     ; 9
    ret
    or      #86h     ;10
    ret
    or      #86h     ;11
    ret
    or      #89h     ;12
    ret
    or      #89h     ;13
    ret
    or      #87h     ;14
    ret
    or      #87h     ;15
    ret
    or      #90h     ;16
    ret
    or      #90h     ;17
    ret
    or      #90h     ;18
    ret
    or      #90h     ;19
    ret
    or      #8Ah     ;20
    ret
    or      #8Ah     ;21
    ret
    or      #8Ah     ;22
    ret
    or      #8Ah     ;23
    ret
    or      #8Dh     ;24
    ret
    or      #8Dh     ;25
    ret

;*******************************************************************************
;*
;* Function:    ow_reset
;*
;* Sends the 1-Wire reset stimulus and checks for 1-Wire slave device presence
;* pulses.
;*
;* Input:       None
;*
;* Output:      ACC = 0 if at least one device responds.
;*
;* Destroys:    OWA, OWD, LC[0], PSF
;*
;*******************************************************************************
ow_reset:

    move    OWA, #OW_COMMAND        ; Kick off a 1-Wire Reset.
    move    OWD, #1

ow_reset_1WR_test:
    move    C, OWD.0                ; Wait for reset done signal.
    jump    C, ow_reset_1WR_test

    move    OWA, #OW_INTERRUPT_FLAGS

    move    LC[0], #1C00h           ; Give the 1-Wire master time to complete
    djnz    LC[0], $                ; a presence detect.

ow_reset_PD_test:
    move    C, OWD.0                ; Check the presence detect complete bit.
    jump    NC, ow_reset_PD_test

    move    ACC, OWD
    and     #2
    ret

;*******************************************************************************
;*
;* Function:    ow_touchbit
;*
;* Sends and receives a single bit of data to/from the 1-Wire bus.
;*
;* Input:       ACC - place the bit to be transmitted in the least 
;*                    significant bit.
;*
;* Output:      ACC - the value read from the 1-Wire bus saved in the
;*                    least significant bit.
;*
;* Destroys:    OWA, OWD, ACC, PSF
;*
;*******************************************************************************
ow_touchbit:

    move    C, #1
    move    OWA, #OW_CONTROL;           ; Place into bit banging mode.
    move    OWD.5, #1

    move    OWA, #OW_TXRX_BUFFER        ; Activate the transmit/receive reg.
    move    OWD, ACC                    ; Write the new transmit value.

    move    OWA, #OW_INTERRUPT_FLAGS

ow_touchbit_RBF_test:                   ; Wait until the transmit completes.
    move    C, OWD.4
    jump    NC, ow_touchbit_RBF_test

    move    OWA, #OW_TXRX_BUFFER        ; Activate the transmit/receive reg.
    move    ACC, OWD                    ; Read what was received.

    move    OWA, #OW_CONTROL;           ; Place back into the default mode.
    move    OWD.5, #0

    ret

;*******************************************************************************
;*
;* Function:    ow_touchbyte
;*
;* Sends and receives a byte of data to/from the 1-Wire bus.
;*
;* Input:       ACC - the byte to be transmitted.
;*
;* Output:      ACC - the value read from the 1-Wire bus.
;*
;* Destroys:    OWA, OWD, PSF
;*
;*******************************************************************************
ow_touchbyte:

    move    OWA, #OW_TXRX_BUFFER        ; Activate the transmit/receive reg.
    move    OWD, ACC                    ; Write the new transmit value.

    move    OWA, #OW_INTERRUPT_FLAGS

ow_touchbyte_RBF_test:                  ; Wait until the transmit completes.
    move    C, OWD.4
    jump    NC, ow_touchbyte_RBF_test

    move    OWA, #OW_TXRX_BUFFER        ; Activate the transmit/receive reg.
    move    ACC, OWD                    ; Read what was received.

    ret

;*******************************************************************************
;*
;* Function:    ow_findfirst
;*
;* Finds the first device on the 1-Wire Net.
;*
;* Input:       DP[0] - points to 8 bytes of space for storing the rom_id of
;*                      the 1-wire device found.
;*              DP[1] - points to 18 bytes of scratch space this function should
;*                      use for its working area.
;*
;* Output:      ACC = 0 if a device was found.
;*
;* Destroys:    A[0] through A[6], AP, APC, LC[0], PSF, DPC
;*
;*******************************************************************************
ow_findfirst::
    move    DPC, #1         ; Set the data pointers to byte mode
    push    DP[1]
    move    @DP[1], #0
    move    @++DP[1], #0
    pop     DP[1]
    call    ow_findnext
    ret

;*******************************************************************************
;*
;* Function:    ow_findnext
;*
;* Performs a general search.  This function continues from the previous search
;* state.  The search state can be reset by using the 'ow_findfirst' function.
;*
;* Input:       DP[0] - points to 8 bytes of space for storing the rom_id of
;*                      the 1-wire device found.
;*              DP[1] - points to 34 bytes of scratch space this function should
;*                      use for its working area.  This data in this area should
;*                      not be changed from a previous call to ow_findfirst or
;*                      between calls to this function.
;*
;* Output:      ACC = 0 if a device was found.
;*
;* Destroys:    A[0] through A[6], AP, APC, LC[0], PSF, DPC
;*
;*******************************************************************************
ow_findnext::

    ; Using the algorithms described in AppNote 192
    ; A[1] = holds pointer to ROM_ID
    ; A[2] = holds pointer to scratch space
    ;        first byte is LastDeviceFlag
    ;        second byte is LastDiscrepency
    ;        next 16 bytes are the search data
    ; A[3] = id_bit_number
    

    move    DPC, #1                 ; Set the data pointers to byte mode
    move    ACC, @DP[1]             ; Check to see if the last device was found
    jump    NZ, ow_findnext_done    ; during the previous search.

    call    ow_reset                ; Do a reset and check if any presence
    jump    NZ, ow_findnext_done    ; pulses were detected.

    move    AP, #0
    move    APC, #0

    move    ACC, #0F0h              ; Send the Search command.
    call    ow_touchbyte

    move    OWA, #OW_COMMAND        ; Activate the search accelerator.
    move    OWD.1, #1

    ; Build the outbound search data

    move    A[1], DP[0]             ; Save for later.
    move    A[2], DP[1]

    move    A[3], #1                ; Set id_bit_number to 1.
    move    ACC, @DP[1]++           ; Don't care about value, just incrementing.
    move    A[4], @DP[1]++          ; Get LastDiscrepency

    move    LC[0], #8               ; Go through the ROM_ID 1 byte at a time and
ow_findnext_byteloop:               ; the search buffer 2 bytes at a time.

    move    A[5], @DP[1]            ; Get the next byte of the search buffer
    move    DP[0], DP[0]
    move    A[6], @DP[0]            ; Get the next byte of the ROM_ID
    move    DP[1], DP[1]

    move    LC[1], #8               ; Process these bytes bit by bit
ow_findnext_bitloop:

    move    ACC, LC[1]              ; If we've processed 4 bits its time to grab
    cmp     #4                      ; the next byte from the search buffer.
    jump    NE, ow_findnext_gotbyte
    move    @DP[1], A[5]            ; Store the new value.
    move    ACC, @DP[1]++           ; Increment the pointer.
    move    A[5], @DP[1]            ; Get the next byte.

ow_findnext_gotbyte:
                                    
    move    ACC, A[3]               ; Compare id_bit_number to LastDiscrepency.
    sub     A[4]

    jump    C, ow_findnext_copyrombit   ; id_bit_number < LastDiscrepency
    jump    Z, ow_findnext_search1      ; id_bit_number == LastDiscrepency
                                        ; else, id_bit_number > LastDiscrepency
ow_findnext_search0:                
                                    ; C must be cleared to get into here.
    move    ACC, A[5]               ; Set the search direction to 0. 
    sra2
    move    A[5], ACC
    jump    ow_findnext_incbit      ; Process next bit.

ow_findnext_copyrombit:

    move    ACC, A[6]               ; Set search direction to ROM_ID bit.
    rrc                             ; Move ROM_ID bit into C.
    jump    NC, ow_findnext_search0 ; Set search direction to 1 or 0.

ow_findnext_search1:

    move    ACC, A[5]               ; Set the search direction to 1.
    sra2
    or      #080h
    move    A[5], ACC

ow_findnext_incbit:

    move    ACC, A[3]               ; Increment id_bit_number.
    add     #1
    move    A[3], ACC

    move    ACC, A[6]               ; Shift down to next bit of ROM_ID.
    sra
    move    A[6], ACC

    djnz    LC[1], ow_findnext_bitloop

    move    @DP[1], A[5]            ; Save new search data value.

    move    ACC, @DP[1]++           ; Point to next byte of search buffer.
    move    DP[0], DP[0]
    move    ACC, @DP[0]++           ; Point to next byte of ROM_ID.
    move    DP[1], DP[1]

    djnz    LC[0], ow_findnext_byteloop

    ; Finished constructing search data
    ; Send it to the 1-Wire controller

    move    ACC, A[2]               ; Set DP[1] to search buffer.
    add     #2
    move    DP[1], ACC
    
    move    LC[0], #16              ; Transmit 16 bytes.
ow_findnext_transmit:
    move    ACC, @DP[1]             ; Get byte from search_buffer.
    call    ow_touchbyte            ; Send it.
    move    @DP[1], ACC             ; Save the result.
    move    ACC, @DP[1]++           ; Increment the pointer.
    djnz    LC[0], ow_findnext_transmit

    move    OWA, #OW_COMMAND        ; Disable the search accelerator.
    move    OWD.1, #0

    ; The search data has been sent, now parse the response data.

    move    DP[0], A[1]             ; Set DP[0] to ROM_ID
    move    DP[1], A[2]             ; Set DP[1] to search data.
    move    ACC, @DP[1]++           
    move    ACC, @DP[1]++           

    move    A[3], #1                ; Set id_bit_number to 1.
    move    A[4], #0                ; Set LastDiscrepency to 0.

    move    LC[0], #8               ; Go through the ROM_ID 1 byte at a time and
ow_findnext_byteloop2:              ; the search buffer 2 bytes at a time.

    move    A[5], @DP[1]++          ; Get the next byte of the search buffer
    move    DP[0], DP[0]
    move    A[6], @DP[0]            ; Get the next byte of the ROM_ID
    move    DP[1], DP[1]

    move    LC[1], #8               ; Process these bytes bit by bit
ow_findnext_bitloop2:

    move    ACC, LC[1]              ; If we've processed 4 bits its time to grab
    cmp     #4                      ; the next byte from the search buffer.
    jump    NE, ow_findnext_gotbyte2
    move    A[5], @DP[1]++          ; Get the next byte.

ow_findnext_gotbyte2:

    move    ACC, A[5]               ; A[5] is current search buffer byte.
    rrc                             ; Get the discrepency bit.
    jump    NC, ow_findnext_nodiscrep
    rrc
    jump    C, ow_findnext_nodiscrep
    move    A[4], A[3]              ; Set LastDiscrepency to id_bit_number.

ow_findnext_nodiscrep:

    move    ACC, A[6]               ; Get the new ROM_ID bit
    move    C, A[5].1
    rrc     
    move    A[6], ACC

    move    ACC, A[5]               ; Shift down the next two bits of search data
    sra2
    move    A[5], ACC
    
    move    ACC, A[3]               ; Increment id_bit_number.
    add     #1
    move    A[3], ACC

    djnz    LC[1], ow_findnext_bitloop2

    move    DP[0], DP[0]                
    move    ACC, A[6]

    RR                              ; We've shifted 8 bits of the new ROM ID
    RR                              ; value into A[6].  On a MAXQ20 system this
    RR                              ; register is 16 bits and the value will be
    RR                              ; in the MSB.  We need to move it to the LSB
	RR								; We'll use 8 rotates instead of an XCH
	RR								; instruction so this code will also work
	RR								; on MAXQ10 systems where the accumulators
	RR								; are only 8 bits.

    move    @DP[0], ACC				; Save new ROM_ID value.
    move    ACC, @DP[0]++			; Point to next byte of ROM_ID.
	move	DP[1], DP[1]				

    djnz    LC[0], ow_findnext_byteloop2

	move	ACC, A[4]				; Save off LastDiscrepency.
	move	DP[1], A[2]
	move	@++DP[1], ACC
	jump	NZ, ow_findnext_crc		; See if last device was found.
	move	@--DP[1], #1			; Set LastDeviceFlag to 1.

ow_findnext_crc:

	move	DP[0], A[1]				; Point to ROM_ID
	call	ow_checkcrc				; Check for a valid ID.

	jump	Z, ow_findnext_done

	move	DP[1], A[2]				; Device not found.
	move	@DP[1], #0				; Set LastDeviceFlag to 0.
	move	@++DP[1], #0			; Set LastDiscrepency to 0.

ow_findnext_done:
	ret

ow_checkcrc:
	move	ACC, #0
	ret

end
