;***************************************************************************
;***************************************************************************
;   ******           THE FOLLOWING ARE SUB-ROUTINES            *******
;   ******             CALLED BY THE MAIN PROGRAM              *******
;***************************************************************************
;***************************************************************************

;*** SWITCH PRESCALER BETWEEN WDT AND RTCC *** (PrScale, NoPrScl) ***
;*** THIS PROCEDURE, DOCUMENTED IN SPEC. SHEET SECTION 9.1, IS INTENDED TO 
;*** PREVENT UNEXPECTED RESET CONDITION

;*** PrScale ROUTINE CALLED AT END OF AttnSig AND Srq SIGNALS
PrScale clrwdt              ; Change prescaler from WDT to RTCC 
        movlw   b'00000001' ; BINARY - set to prescale RTCC
        option              ; Clear 4th bit from right to select RTCC
        clrf    TMR0       ; last 3 bits set prescale value as 1:4
        retlw   NULL        ;   this gives a good ratio to monitor the
                            ;   timing for Reset and Attention signals and
                            ;   the 2nd Application Task

;*** NoPrScl ROUTINE CALLED AT BEGINNING OF SyncSig AND END OF Srq SIGNALS
NoPrScl clrf    TMR0       ; Change prescaler from RTCC to WDT 
        movlw   b'00001000' ; Set 4th bit from right to select WDT
        option
        clrwdt
        movlw   b'00001000' 
        option
        retlw   NULL

;***************************************************************************

;*** GET INCOMING BIT & INTERPRET WHETHER IT'S A '1' OR A '0' *** (Get_Bit) ***
;*** Get_Bit CALLED BY COMMAND AND LISTEN ROUTINES
; Get the bit, find out whether it's less than or greater than 50 usecs, 
;   if < than 50 usecs, it's a '1' bit
;   if > than 50 usecs, it's a '0' bit
; if it's a '1' bit, set LSB in the reg. pointed to by the FSR (Command Byte)
; if it's a '0' bit, do nothing to the LSB
; then look for the end of the Bit Cell (104 usecs max.)
; if the maximum Bit time of (72 usecs) or maximum Bit Cell time is exceeded,
;   abort to the Attn Signal

Get_Bit movf    TMR0,W     ; Check the time, then check if the line went high:
        subwf   TimeVar,W   ; See if more than BIT_TST usecs have passed
        btfss   STATUS,C;   if not, check whether the line went high
        goto    AttnSig     ;   if so, abort to the Attn Signal
        btfss   PORTA,ADB  ; Check whether the line went high
        goto    Get_Bit     ;   if line is still low, loop again
        movlw   BIT_TST     ;   if line went high, see if it's a '1' or a '0'
        movwf   TimeVar     ; as the bit has not yet been determined yet,
        bcf     INDF,LSB  ;   ensure the LSB in the indirect address is '0'
        movf    TMR0,W     ; Get the time
        subwf   TimeVar,W   ;   if time < 50 usecs, it's a '1' bit
        btfsc   STATUS,C;   if time > 50 usecs and < 72, it's a '0' bit
        bsf     INDF,LSB  ; if it's a 1, set LSB in the address FSR points to
        movlw   BITCELL     ; Check whether the Max. Bit Cell time of 104 usecs
        movwf   TimeVar     ;   has been exceeded
CellChk movf    TMR0,W     ; Check the time, then check the line
        subwf   TimeVar,W   ; See if more than Max. Bit Cell usecs have passed
        btfss   STATUS,C;   if not, look for the line to go low again
        goto    AttnSig     ;   if so, abort to the Attn Signal or Reset
        btfsc   PORTA,ADB  ; Check the line for the start of another bit
        goto    CellChk     ;   if the line is still high, loop CelChk1 again
        clrf    TMR0       ;   if the line went low, clear the RTCC & return
        retlw   NULL        ;       for another bit or to interpret the Command

;***************************************************************************
;*** DETERMINE IF THIS IS A GLOBAL COMMAND TO ALL DEVICES  *** (Globals) ***
;*** Globals CALLED BY AddrChK

Globals movf    TmpReg2,W   ; Check whether the Command is for all devices 
        xorlw   C_RES_1     ; retrieve the Command Type (the upper 2 bits of 
        btfsc   STATUS,Z    ;   the Command nibble)
        goto    Reserv1     ; test for this being the first Reserved Command
        movf    TmpReg1,W   ; retrieve the whole Command Nibble
        xorlw   C_RES_2     ; test for this being the second Reserved Command
        btfsc   STATUS,Z
        goto    Reserv2
        movf    TmpReg1,W   ; retrieve the whole Command Nibble
        xorlw   C_RES_3     ; test for this being the third Reserved Command
        btfsc   STATUS,Z
        goto    Reserv3
        xorlw   C_RESET     ; test for this being Reset Command
        btfsc   STATUS,Z
        goto    Reset
        retlw   NULL

;***************************************************************************

;*** MASK OUT COMMAND NIBBLE AND REG.# BITS FROM THE COMMAND *** (MaskCmd) ***
; NOTE: This routine should only be called once during any single ADB 
; transaction, from either AddrChk or CmmdChk

MaskCmd movf    CmdByte,W   ; Mask the Command to save the Data Reg. # bits &
        andlw   CMDNIBL     ;   the Command Type bits (Listen, Talk, etc.):
        movwf   TmpReg1     ; save the Command nibble
        andlw   CMDTYPE     ; mask the upper 2 Command Type bits (Talk, etc.)
        movwf   TmpReg2     ; save the upper 2 Command Type bits
        movf    CmdByte,W   ; extract the Data Register number:
        andlw   REGMASK     ;   mask out Data Reg. number from Command Nibble
        movwf   RegNum      ;   save the Data Reg. bits
        movwf   FSR         ;   save pointer to Data Reg. in File Select Reg.  
                            ;     in order to setup RAM address where start
                            ;       of Data for this Reg. will be stored
SaveRAM bcf     STATUS,C; clear Carry bit so it doesn't wrap around
        rlf     FSR,F       ;   multiply by 2 to get 1st Byte of RAM addr
        bsf     FSR,03h     ;   add array offset for Send/Receive/Flush Reg.
        movf    FSR,W       ;       by setting bit of 1st RAM address, which
        andlw   FSRMASK     ;           is ORG'd in ADB.EQU equates
        movwf   RAMaddr     ;   mask out the RAM address of Data Reg. Number
        retlw   NULL        ;   save RAM address of Data Reg. and return

;***************************************************************************

;*** ISSUE A SERVICE REQUEST IF NECESSARY *** (Srq; may call LineLow) ***
;*** CALLED BY AddrChk
; see if the Srq Flag is set, if not, return, otherwise:
;   change the prescaler to RTCC since this takes longer than 255 usecs,
;   load the SRQTIME of 300 usecs into the TimeVariable,
;   call LineLow to:
;       keep checking the time to see if 300 usecs have passed,
;       let the line go high again, 
;       and see if the line is high, and if not, abort, if it is,
;   change the prescaler back to WDT, and return 

Srq     btfss   Flags2,F2Srq; see if the Srq flag is set, 
        retlw   NULL        ;   if not, return
        call    PrScale     ; switch the prscaler to RTCC
        movlw   TRI_OUT     ; tri-state PORTA to make the ADB an output
        tris    PORTA      
        movlw   SRQ_MAX     
        call    LineLow
        call    NoPrScl     ; change the prescaler back to WDT
        retlw   NULL
    
;***************************************************************************

;*** Tlt - TIME FROM STOP BIT TO START BIT ***  (Tlt) ***
;*** CALLED BY EITHER Talk OR Listen ROUTINES
; Loop checking the time, then checking the line to see if it went low
;   if at any time the line goes low,
;       see if this is a Talk Command, 
;           if it is a Talk Commmand, go to the Collision routine
;   if the line goes low before the minimum Tlt time, abort to Attn Signal
;   if the line is high longer than TLT_Min usecs, 
;       see if this is a Talk Command, and if it is, wait for the mid-point,
;           and return to Send the Start Bit, Data Bytes, & the Stop Bit
;       if it's not a Talk Command, see if it's a Listen Command, and if so,
;           load Tlt_Max for TimeVariable, and look for the line to go 
;               low as the beginning of the Start Bit,
;       if more than Tlt_Max usecs pass, abort to Attn Signal
;   if the line goes low and this is a Listen Command,
;       clear the RTCC & return to get the rest of the Start Bit

Tlt     movlw   TLT_MIN     ; Look for Stop-to-Start-Time, Tlt
        movwf   TimeVar     ; Check the time, then check the line
TltChk1 movf    TMR0,W     ; See if more than TLT_MIN usecs have passed
        xorwf   Random,F    ; (ensure the Talk R3 address is Random with XOR)
        subwf   TimeVar,W   ;   by checking whether Carry bit is set  
        btfss   STATUS,C;     after subtraction                     
        goto    ChkFlag     ; if TLT_MIN usecs passed, see what Command this is
        btfsc   PORTA,ADB  ; if not, check whether the line went low
        goto    TltChk1     ;   if the line is still high, keep looping
        btfsc   Flags1,F1Talk;  if line went low, see if this is a Talk Command
        goto    Collisn     ;     if it is, there was a Collision, abort
        movf    TMR0,W     ;   otherwise, check the time
        subwf   TimeVar,W   ;      see if TLT_MIN usecs passed,
        btfss   STATUS,C;      if not, abort to Attn Signal, too little
        goto    AttnSig     ;           time passed when the line went low
        clrf    TMR0       ;   if it's not a Talk Command, clear the RTCC and
        retlw   NULL        ;       return for the rest of the Start Bit 

ChkFlag btfsc   Flags1,F1Talk; Check whether to Talk or Listen
        goto    TltTalk     ;   if Talk, wait for mid-point of Tlt time
        btfss   Flags1,F1Lstn;  if Listen, continue to look for Start Bit
        retlw   NULL        ;  if neither flag is set, abort, something's wrong
        movlw   TLT_MAX     ;  Load TimeVariable to check for upper limit 
        movwf   TimeVar     ;       of Tlt time
TltChk2 movf    TMR0,W     ; See if TLT_MAX usecs have been exceeded
        subwf   TimeVar,W   ;   by checking whether Carry bit is set  
        btfss   STATUS,C;       after subtraction                     
        goto    AttnSig     ;   if so, abort to Attn Signal
        btfsc   PORTA,ADB  ; if not, check whether the line went low
        goto    TltChk2     ;   if line is still high, check the time again
        btfsc   Flags1,F1Talk;  if line went low, see if this is a Talk Command
        goto    Collisn     ;       if so, there was a Collision
        clrf    TMR0       ;       if it's not a Talk Command, return to get
        retlw   NULL        ;       the rest of the Start Bit from Host

TltTalk movlw   TLT_MID     ; Load TimeVariable so Talk will send Start Bit at 
        movwf   TimeVar     ;   about the mid-point of the Tlt 
TltChk3 movf    TMR0,W     ; See if TLT_MID usecs have been exceeded
        subwf   TimeVar,W   ;   by checking whether Carry bit is set  
        btfss   STATUS,C;       after subtraction                     
        retlw   NULL        ; if time was exceeded, return to send Start Bit
        btfsc   PORTA,ADB  ; if not, check whether the line went low
        goto    TltChk3     ;   if line is still high, check the time again
        goto    Collisn     ;   if the line went low, abort to Collision

;***************************************************************************

;*** MAKE LINE GO LOW TIME IN TimeVar AS A '1' OR '0' BIT *** (LineLow) ***
;*** CALLED BY Talk OR Srq

LineLow movwf   TimeVar     ; 
Low_Tmp movf    TMR0,W     ; Check the clock,                       
        subwf   TimeVar,W   ;  loop until TimeVar usecs have passed 
        btfsc   STATUS,C;
        goto    Low_Tmp     ;
        movlw   TRI_IN      ; Tri-state PORTA to make ADB line an input again
        tris    PORTA      ;   and let the line go high
        clrf    TMR0       ;   and clear RTCC
        nop                 ; Allow the ADB Port line to stabilize
        nop                 ; Allow the ADB Port line to stabilize
        btfss   PORTA,ADB  ; check if the line is still low, if so, a
        goto    Collisn     ;   Collision occurred
        retlw   NULL        ; if not, return to load high time for rest of bit

;*** MAKE LINE GO HIGH FOR REST OF BIT CELL TIME IN TimeVar *** (LineHi) ***
;*** CALLED BY Talk

LineHi  movwf   TimeVar     ; Let the line go high for a pre-designated time
Hi_Tmp  movf    TMR0,W     ; Check the clock, 
        subwf   TimeVar,W   ;   loop until TimeVar usecs have passed
        btfsc   STATUS,C; 
        goto    Hi_Tmp      ;
        btfss   PORTA,ADB  ;  check if the line is still high,
        goto    Collisn     ;    if not, a Collision occurred, Abort
        btfsc   Flags1,F1Stop; if this is the end of the Data Stop Bit, don't
        retlw   NULL        ;    let the line go low again, just return
        movlw   TRI_OUT     ;  if still high, start sending a bit to the Host
        tris    PORTA      ;  tri-state PORTA to make the ADB an output and
        clrf    TMR0       ;     return
        retlw   NULL        ;  

;***************************************************************************
;***************************************************************************
;   ******                  END OF SUB-ROUTINES                *******
;***************************************************************************
;***************************************************************************
