        ; fefifoh.asm: new improved fefifo handler routine
#define HANGDEBUG 1
;----------------------------------------------------
FC0 macro
; FRAMECOUNT is 0
; INDF0 points to FRAME0LO, 1+INDF0 points to FRAME0HI.
; The entry at INDF0 is expected to be a start bit.

        btfsc FRAME0HI,0        ; D8 should be clear...
        goto fc0badstartbit
        movlw 1                 ; lsbyte should be > 1 ...
        cpfsgt FRAME0LO
        goto fc0badstartbit
        movlw 8                 ; ...and < 8
        cpfslt FRAME0LO
        goto fc0badstartbit

                                ; passes sanity check
        incf FRAMECOUNT,F
        movpf INDF0,FRAMM0LO    ; copy to FRAMM
        incf FSR0,F
        movpf INDF0,FRAMM0HI
                        ; The front end FIFO keeps hanging:
                        ;  SI keeps toggling, but OR=HF=FULL=0
                        ; From observing the LEDs, I seem to get a bunch of
                        ; LEDBIT_BADSTARTBITs just before this happens
                        ; Resetting the FIFO seems to fix it, so we'll keep
                        ; track of the # of CONSECutive Bad Start BitS and,
                        ; when it gets too high, assume the FIFO is hung
                        ; and reset it.
        clrf CONSECBSBS,F
        goto fc0end

fc0badstartbit
        movlw LEDBIT_BADSTARTBIT
        call ledSet
        call ledClear
                                ; start of hack
        incf CONSECBSBS,F       ; keep count of consecutive baddies
        movlw .20               ; thresh should be >12
        cpfsgt CONSECBSBS
        goto fc0end
                                ; Too many consecutive bad start bits.
                                ; Assume that the FIFO is hung and reset it.
        call frontEndStop
        call frontEndStart
        goto fefifohdone        ; needn't SO after a reset
                                ; end of hack
fc0end
        SHIFTITOUT              ; inline version of shiftitout()
        goto fefifohdone

endm    ; of FC0 macro for start bit
;----------------------------------------------------
FC1 macro
; FRAMECOUNT is 1
; INDF0 points to FRAME1LO, 1+INDF0 points to FRAME1HI.
; The entry at INDF0 is expected to be the space following a start bit

        clrf FRAMEISDUMMY,F
        clrf CVTTODUMMY,F
        btfsc FRAME1HI,0        ; if D8 is set...
        goto fc1frameisdummy
        movlw 0xC0              ; ... or lsbyte is big enough...
        cpfslt FRAME1LO
                        ; twix
                        ; start of hack: seems that the D8 bit is sometimes lost
                        ; so a 10A comes out as 00A, which gets treated as a
                        ; real frame.
        goto fc1frameisdummy
        movlw 0x20
        cpfslt FRAME1LO         ; if the space is apparently way too short,
        goto fc1dcd             ; then let's assume that the D8 bit got lost
        bsf FRAME1HI,0          ; and this is really a dummy frame. Set D8.
       ;bsf CVTTODUMMY,0
                        ; end of hack
fc1frameisdummy                 ; ... it's a dummy
        bsf FRAMEISDUMMY,0

fc1dcd                  ; fc1DummyCheckDone
        incf FRAMECOUNT,F       ; sanity check complete.
        movpf INDF0,FRAMM1LO    ; copy to FRAMM
        incf FSR0,F
        movpf INDF0,FRAMM1HI
        SHIFTITOUT
        goto fefifohdone

endm    ; of FC1 macro for space following start bit
;----------------------------------------------------
FC2 macro
; FRAMECOUNT is 2
; INDF0 points to FRAME2LO, 1+INDF0 points to FRAME2HI.
; The entry at INDF0 is expected to be the electrode selection pulse

        btfss FRAME2HI,0        ; check D8
        goto fc2d8clr           ; D8 is clear

        clrf FRAME2HI,F         ; D8 is set. As a hack, just clear it.
        bsf DEBFLAG,0           ; debugging fc2d8set problem: check it before shipping
fc2d8clr

        tstfsz FRAME2LO         ; A hack: if FRAME2LO=0, then we won't make it
        goto fc2f2lo0           ; out of the PAIRNO loop. This should never
       ;movlw LED_FRAME2LO0     ; happen, but it does, so deal with it.
       ;call ledFastFlash
        goto fc2bad
fc2f2lo0

                        ; convert pulse width to PAIRNO
                        ; (formerly done in updateMap())
        movlw 0xFF                      ; 0xFF will increment to 0
        movwf PAIRNO
        movlw MAXPAIRWIDTHPLUS  ; 0xA8    Max pulse2 width + PAIRWIDTHDELTA/2
fc2a
        incf PAIRNO,F
        addlw PAIRWIDTHDELTA    ; 0xF8    Overflow simulates subtract.
        cpfsgt FRAME2LO
        goto fc2a

                ; PAIRNO contains electrode number
        movlw .19               ; sanity check: 0<=PAIRNO<=19
        cpfsgt PAIRNO           ; unsigned compare. Needn't check if PAIRNO<0
        goto fc2pairnook
                        ; PAIRNO is out of range
        movlw LED_BADPAIRNO
        call ledFlash
        goto fc2bad
fc2pairnook

                        ; add MC_SHIFT to PAIRNO
        movfw MC_SHIFT
        addwf PAIRNO,W
        movwf NEWPAIRNO         ; NEWPAIRNO=PAIRNO+MC_SHIFT

        movlw .19               ; sanity check: 0<=NEWPAIRNO<=19
        cpfsgt NEWPAIRNO
        goto fc2newpairnook
                        ; NEWPAIRNO is out of range
        movlw LEDBIT_BADNEWPAIRNO
        call ledSet
        call ledClear
        bsf CVTTODUMMY,0        ; FRAMM will be filled with a dummy image
        clrf NEWPAIRNO,F
fc2newpairnook

                        ; Check whether MC_T[NEWPAIRNO] is 0
                        ; If so, then there's never been a frame on this
                        ; electrode pair, so we can't 'shift onto it'
        movlw MC_T0
        addwf NEWPAIRNO,W
        movwf FSR0
        movpf INDF0,NEWTHRESH   ; save now and we won't recalc later
        tstfsz NEWTHRESH        ; NEWTHRESH is MC_T[NEWPAIRNO]
        goto fc2newthreshok
                                ; Leave NEWTHRESH at 0.
        bsf CVTTODUMMY,0        ; Result will be overwritten w/ dummy image.

fc2newthreshok                  ;

        movlw MC_R0             ; might as well save NEWRANGE now too.
        addwf NEWPAIRNO,W
        movwf FSR0
        movpf INDF0,NEWRANGE

                                ; calc new pulse width from NEWPAIRNO
        movlw PAIRWIDTHDELTA    ; #define'd as negative
        mulwf NEWPAIRNO         ; PRODL = -8*NEWPAIRNO
        movlw MAXPAIRWIDTH
        addwf PRODL,W           ; wreg = MAXPAIRWIDTH - 8*NEWPAIRNO
        movwf FRAMM2LO          ; put result in FRAMM
        clrf FRAMM2HI,F

        incf FRAMECOUNT,F
        goto fc2end

fc2bad
 if 1
                        ; the PC may have registered for this event
        btfss INTERCEPTEVENT,0  ; INTERCEPTEVENT boilerplate
        goto fc2badA            ;       "             "
        call frontEndStop       ;       "             "
        call visibleDelay       ;       "             "
        clrf INTERCEPTEVENT,F   ;       "             "
        call busHiZ             ;       "             "
        call visibleDelay       ;       "             "

        movlw .7                ; total # of bytes to be sent
        call busSendPCByte
        movlw 1                 ; Data ID byte
        call busSendPCByte
        movfw FRAME0HI          ; frame fragment
        call busSendPCByte
        movfw FRAME0LO
        call busSendPCByte
        movfw FRAME1HI
        call busSendPCByte
        movfw FRAME1LO
        call busSendPCByte
        movfw FRAME2HI
        call busSendPCByte
        movfw FRAME2LO
        call busSendPCByte
        call busReadPCByte      ; get ACK byte into wreg

        call busFEST            ; INTERCEPTEVENT boilerplate
        call frontEndStart      ;       "             "
fc2badA                         ;       "             "
 endif
        movlw LEDBIT_BADFC2
        call ledSet
        call ledClear
        clrf FRAMECOUNT,F       ; discard rest of frame
fc2end

        SHIFTITOUT
        goto fefifohdone

endm    ; of FC2 macro for electrode selection pulse
;----------------------------------------------------
FC3 macro
; FRAMECOUNT is 3
; INDF0 points to FRAME3LO, 1+INDF0 points to FRAME3HI.
; The entry at INDF0 is a generic inter-pulse space

        btfsc FRAME3HI,0        ; if D8 is set...
        goto fc3bad             ; ... it's out of range

        incf FRAMECOUNT,F
        movfw FRAME3LO          ; update FRAMM
        movwf FRAMM3LO
        clrf FRAMM3HI,F
        goto fc3end

fc3bad
        movlw LEDBIT_BADFC3
        call ledSet
        call ledClear
        clrf FRAMECOUNT,F       ; discard rest of frame
fc3end
        SHIFTITOUT              ; inline version of shiftitout()
        goto fefifohdone

endm    ; of FC3 macro for generic space
;----------------------------------------------------
FC4 macro
; FRAMECOUNT is 4
; INDF0 points to FRAME4LO, 1+INDF0 points to FRAME4HI.
; The entry at INDF0 is expected to be the unknown pulse

        btfsc FRAME4HI,0        ; if D8 is set...
        goto fc4bad             ; ... it's out of range

        incf FRAMECOUNT,F
        movfw FRAME4LO          ; update FRAMM
        movwf FRAMM4LO
        clrf FRAMM4HI,F
        goto fc4end

fc4bad
        movlw LEDBIT_BADFC4
        call ledSet
        call ledClear
        clrf FRAMECOUNT,F       ; discard rest of frame
fc4end
        SHIFTITOUT
        goto fefifohdone

endm    ; of FC4 macro for unknown pulse
;----------------------------------------------------
FC5 macro
; FRAMECOUNT is 5
; INDF0 points to FRAME5LO, 1+INDF0 points to FRAME5HI.
; The entry at INDF0 is a generic inter-pulse space

 if 0
        clrf FRAMECOUNT,F       ; debug
        goto fc5end
 endif

        btfsc FRAME5HI,0        ; if D8 is set...
        goto fc5bad             ; ... it's out of range

        incf FRAMECOUNT,F
        movfw FRAME5LO          ; update FRAMM
        movwf FRAMM5LO
        clrf FRAMM5HI,F
        goto fc5end

fc5bad
        movlw LEDBIT_BADFC5
        call ledSet
        call ledClear
        clrf FRAMECOUNT,F       ; discard rest of frame
fc5end
        SHIFTITOUT              ; inline version of shiftitout()
        goto fefifohdone

endm    ; of FC5 macro for generic space
;----------------------------------------------------
FC6 macro
; FRAMECOUNT is 6
; INDF0 points to FRAME6LO, 1+INDF0 points to FRAME6HI.
; The entry at INDF0 is expected to be the stim strength

        clrf ADDER,F
        btfsc FRAME6HI,0        ; if D8 is set...
        goto fc6big             ; ... it's probably a dummy frame. Leave as-is.
                        ; twix
                        ; start of hack: sometimes D8 bit seems to get dropped.
                        ; when FRAME6 is 100 and D8 is dropped, it becomes 000
                        ; and the slave thinks it's an inter-frame NULL and
                        ; flags it.
        movlw 0x30
        cpfslt FRAME6LO ; if it's a lot less than MINSTIMSTRENGTH (=0x42),
        goto fc6eoh     ; then assume that D8 got dropped and restore it.
        bsf FRAME6HI,0
        goto fc6big
fc6eoh                  ; end of hack

                        ; calc ADDER value
        movlw MINSTIMSTRENGTH
        cpfsgt FRAME6LO
        goto fc60
        subwf FRAME6LO,W        ; wreg (=ADDER) = (FRAME6LO-MINSTIMSTRENGTH)/4
        rrcf WREG,F             ; ADDER /=4 (4 counts on pulse 4 correspond
        rrcf WREG,F             ;  to 1 count for pulses 5,6 (approx))
        bcf WREG,6
        bcf WREG,7
        movwf ADDER
fc60
        incf FRAMECOUNT,F
                                ; FRAMM6xx is filled in within the FC8 macro
        goto fc6end

fc6big
        incf FRAMECOUNT,F       ; do nothing: ought to assert FRAMEISDUMMY
fc6end
        SHIFTITOUT
        goto fefifohdone

endm    ; of FC6 macro for stim strength pulse
;----------------------------------------------------
FC7 macro
; FRAMECOUNT is 7
; INDF0 points to FRAME7LO, 1+INDF0 points to FRAME7HI.
; The entry at INDF0 is a generic inter-pulse space

        btfsc FRAME7HI,0        ; if D8 is set...
        goto fc7bad             ; ... it's out of range

        incf FRAMECOUNT,F
        movfw FRAME7LO          ; update FRAMM
        movwf FRAMM7LO
        clrf FRAMM7HI,F
        goto fc7end

fc7bad
        movlw LEDBIT_BADFC7
        call ledSet
        call ledClear
        clrf FRAMECOUNT,F       ; discard rest of frame
fc7end
        SHIFTITOUT              ; inline version of shiftitout()
        goto fefifohdone

endm    ; of FC7 macro for generic space
;----------------------------------------------------
FC8 macro
; FRAMECOUNT is 8
; INDF0 points to FRAME8LO, 1+INDF0 points to FRAME8HI.
; The entry at INDF0 is expected to be the stim level

        btfsc FRAME8HI,0        ; if D8 is set...
        goto fc8oor             ; ... it's out of range.

        movpf INDF0,FRAME10LO   ; FRAME10 is always a copy of FRAME8
        clrf FRAME10HI,F

                        ; if frame is a dummy. no need to update map or convert
        btfss FRAMEISDUMMY,0
        goto fc8notdummy
        movfw FRAME6LO
        movwf FRAMM6LO
        movpf INDF0,FRAMM8LO
        movpf INDF0,FRAMM10LO
        movfw FRAME6HI
        movwf FRAMM6HI
        clrf FRAMM8HI,F
        clrf FRAMM10HI,F
        incf FRAMECOUNT,F
        goto fc8end

fc8notdummy

                        ; calc STIMLVL for this frame
        movfw ADDER             ; was calc'ed in FC6
        subwf FRAME8LO,W        ; wreg = FRAME8LO - (adder)
        movwf STIMLVL

                        ; update map threshold level
        movlw MAPT0
        addwf PAIRNO,W
        movwf FSR0              ; FSR0 points to current electrode's T value
        tstfsz INDF0            ; if 1st occurrence of this PAIRNO ...
        goto fc8t1
        movfp STIMLVL,INDF0     ; ... MAPTx=stim
        goto fc8c0
fc8t1
        movfw STIMLVL
        cpfslt INDF0            ; if MAPT[PAIRNO] > stim ...
        movwf INDF0             ; ... MAPTx=stim

fc8c0                   ; update map comfort level
        movlw MAPC0
        addwf PAIRNO,W
        movwf FSR1              ; FSR1 points to current electrode's C value
        tstfsz INDF1            ; if 1st occurrence of this PAIRNO ...
        goto fc8c1
        movfp STIMLVL,INDF1     ; ... MAPCx=stim
        goto fc8r0
fc8c1
        movfw STIMLVL
        cpfsgt INDF1            ; if MAPC[PAIRNO] < stim ...
        movwf INDF1             ; ... MAPCx=stim

fc8r0                   ; update map range value
        movfw INDF0             ; wreg=MAPTx
        subwf INDF1,W           ; wreg=MAPCx-MAPTx
        movwf PUSHW             ; save
        movlw MAPR0
        addwf PAIRNO,W
        movwf FSR1              ; FSR1 points to MAPRx
        movfp PUSHW,INDF1       ; update MAPRx
                        ; done updating map

                        ; proceed with frame modifications
        movfw INDF0             ; still points at MAPT[PAIRNO]
        subwf STIMLVL,W
        movwf NUMERATOR         ; NUMERATOR = STIMLVL-MAPT[PAIRNO]
        movpf INDF1,DENOMINATOR ; DENOMINATOR = MAPR[PAIRNO]

        DIVLUT NUMERATOR,DENOMINATOR    ; macro returns 1-byte quotient in wreg

         movwf WBEFOREQ         ;
        call qFactor            ; adjust for Q-factor
         movwf WAFTERQ          ;
        mulwf NEWRANGE          ; PRODH contains actual offset into new range

#if 0
        ; Volume adjustment was moved to take place during apply-button
        ;  processing in regen_MCData().
        ; This way, processing is not time-critical.
                ; volume control scales between 0 & VMAX where VMAX < 2.
                ; The calculation is: STIM = STIM*2*POTVAL * VMAX/2
                ; The STIM*2*POTVAL is implemented as:
                ;  Multiply STIMLVL by MC_VOL
                ;  shift PRODH left by 1,
                ;  copy PRODL[7] to PRODH[0]
                ; The '*VMAX/2' is implemented by coding a fractional ratio
                ; at compile time. This is VRATIO = 255*VMAX/2
                ; For e.g.: if VMAX=1.23, then VRATIO=255*1.23/2=157=x9D
                ;  and a POTVAL of 255/1.23=207=xD0 gives 'identity' volume
#define VRATIO 0x9D
        movpf PRODH,wreg        ; start with offset into NEWRANGE
        mulwf MC_VOL            ; jump thru hoops to 'multiply' by MC_VOL
        rlcf PRODH,W
        bcf WREG,0
        btfsc PRODL,7
        bsf WREG,0
        movwf TMP
        movlw VRATIO            ; multiply by VRATIO
        mulwf TMP               ; PRODH contains volume-adjusted offset
        movpf PRODH,wreg
        addwf NEWTHRESH,W       ; wreg contains new STIM value.
        movwf NEWSTIMLVL
#else
                ; version does not perform volume adjustment:
        movpf PRODH,wreg
        addwf NEWTHRESH,W       ; wreg contains new STIM value.
        movwf NEWSTIMLVL
#endif

        movlw MINSTIMLVL        ; check whether stim is in range
        cpfslt NEWSTIMLVL
        goto fc8mf1
                        ; NEWSTIMLVL < MINSTIMLVL: need adder compensation
        movfw NEWSTIMLVL
        sublw MINSTIMLVL        ;     WREG = MINSTIMLVL - NEWSTIMLVL
       ;negw NEWADDER,0         ; had sign wrong 10/28/96
        movwf NEWADDER          ; NEWADDER = MINSTIMLVL - NEWSTIMLVL

        rlcf NEWADDER,F         ; NEWADDER *=4
        rlcf NEWADDER,F         ; 4 counts of FRAME6 correspond to 1 count
        bcf NEWADDER,0          ; for FRAME8,10 (approx)
        bcf NEWADDER,1          ; (this should also occur in updateMap)

        movlw MINSTIMLVL
        movwf FRAMM8LO          ; fill in stim level pulse widths
        movwf FRAMM10LO         ;

        movlw MINSTIMSTRENGTH
        addwf NEWADDER,W
        movwf FRAMM6LO          ; fill in stim strength pulse width
        goto fc8mf2
fc8mf1
                        ; NEWSTIM > MINSTIMLVL: no need for compensation.
        movfw NEWSTIMLVL
        movwf FRAMM8LO
        movwf FRAMM10LO
        movlw MINSTIMSTRENGTH
        movwf FRAMM6LO
fc8mf2
        clrf FRAMM6HI,F
        clrf FRAMM8HI,F
        clrf FRAMM10HI,F

                        ; pair selection pulse width already done in FC2
                        ; frame is fully converted.

        incf FRAMECOUNT,F
        goto fc8end

fc8oor                  ; out of range
        movlw LEDBIT_BADFC8
        call ledSet
        call ledClear
        clrf FRAMECOUNT,F
fc8end
        SHIFTITOUT
        goto fefifohdone

endm    ; of FC8 macro for stim level pulse
;----------------------------------------------------
FC9 macro
; FRAMECOUNT is 9
; INDF0 points to FRAME9LO, 1+INDF0 points to FRAME9HI.
; The entry at INDF0 is the space between the 2 equal stim lvl pulses

        btfsc FRAME9HI,0        ; if D8 is set...
        goto fc9bad             ; ... it's out of range

        movpf INDF0,FRAMM9LO
        clrf FRAMM9HI,F

        incf FRAMECOUNT,F
        SHIPTOSLAVE     ; don't wait for FRAME10. It's been forced = to FRAME8
        goto fc9end

fc9bad
        movlw LEDBIT_BADFC9
        call ledSet
        call ledClear
        clrf FRAMECOUNT,F       ; discard rest of frame
fc9end
        SHIFTITOUT              ; inline version of shiftitout()
        goto fefifohdone

endm    ; of FC9 macro for last space
;----------------------------------------------------
FC10 macro
; FRAMECOUNT is 10
; INDF0 points to FRAME10LO, 1+INDF0 points to FRAME10HI.
; The entry at INDF0 is the width of the 2nd stim pulse
; All processing has been done within the FC8 macro, so
; this is kind of a dummy routine.

        incf FRAMECOUNT,F
        SHIFTITOUT              ; inline version of shiftitout()
        goto fefifohdone

endm    ; of FC10 macro for 2nd stim pulse
;----------------------------------------------------
FC11 macro
; FRAMECOUNT is 11
; INDF0 points to FRAME11LO, 1+INDF0 points to FRAME11HI.
; The entry at INDF0 is the width of the space following a frame.

        clrf FRAMECOUNT,F       ; end of frame. Wrap.
        SHIFTITOUT
        goto fefifohdone

endm    ; of FC11 macro for space following frame
;----------------------------------------------------
FCX macro
        clrf FRAMECOUNT,F
        SHIFTITOUT
        goto fefifohdone
endm    ; of FCX macro for debug
;----------------------------------------------------
SHIFTITOUT macro
; this inline code shifts an entry out of the FEFIFO

        movlb 1                 ; bank 1
        bsf PORTC,6             ; set SO bit high
       ;nop
        bcf PORTC,6

endm    ; of SHIFTITOUT
;----------------------------------------------------
SHIPTOSLAVE macro
; We've just processed FRAME9 space and we want to ship this frame off ASAP
; Largely lifted from old shipOrigFrame()


 if 1
        btfss DEBFLAG,0
        goto stsdebflagclr
                        ; DEBFLAG is set
        clrf DEBFLAG,F
                        ; the PC may have registered for this event
        btfss INTERCEPTEVENT,0  ; INTERCEPTEVENT boilerplate
        goto STSIEA             ;       "             "
        call frontEndStop       ;       "             "
        call visibleDelay       ;       "             "
        clrf INTERCEPTEVENT,F   ;       "             "
        call busHiZ             ;       "             "
        call visibleDelay       ;       "             "

        movlw .21               ; total # of bytes to be sent
        call busSendPCByte
        movlw 2                 ; Data ID byte
        call busSendPCByte
        movfw FRAME0HI          ; frame fragment
        call busSendPCByte
        movfw FRAME0LO
        call busSendPCByte
        movfw FRAME1HI
        call busSendPCByte
        movfw FRAME1LO
        call busSendPCByte
        movfw FRAME2HI
        call busSendPCByte
        movfw FRAME2LO
        call busSendPCByte
        movfw FRAME3HI
        call busSendPCByte
        movfw FRAME3LO
        call busSendPCByte
        movfw FRAME4HI
        call busSendPCByte
        movfw FRAME4LO
        call busSendPCByte
        movfw FRAME5HI
        call busSendPCByte
        movfw FRAME5LO
        call busSendPCByte
        movfw FRAME6HI
        call busSendPCByte
        movfw FRAME6LO
        call busSendPCByte
        movfw FRAME7HI
        call busSendPCByte
        movfw FRAME7LO
        call busSendPCByte
        movfw FRAME8HI
        call busSendPCByte
        movfw FRAME8LO
        call busSendPCByte
        movfw FRAME9HI
        call busSendPCByte
        movfw FRAME9LO
        call busSendPCByte
        call busReadPCByte      ; get ACK byte into wreg

        call busFEST            ; INTERCEPTEVENT boilerplate
        call frontEndStart      ;       "             "
STSIEA                          ;       "             "

stsdebflagclr
 endif

        btfsc CVTTODUMMY,0      ; overwrite FRAMM with dummy values
        call createDummyFramm

        call busHiZ             ; put bus into HiZ

        movlb 1                 ; bank 1 for PORTD
        clrf DDRD,F             ; configure PORTD bits as outputs

                        ; Precede frame with a null entry
        clrf PORTD,F
        movlb 0                 ; bank 0 for PORTA

        bcf RA3                 ; apply a 0 to D8 of '823
        bsf RA2                 ; apply rising edge to '823 CP and FIFO SI
        bcf RA2                 ; apply falling edge to '823 CP and FIFO SI

                        ; for this to work, FRAME layout must be:
                        ;     FRAME0LO
                        ;     FRAME0HI
                        ;     FRAME1LO
                        ;     ...
                        ;     FRAMExxHI

        movlw .11               ; # of FIFO entries to ship
        movwf TMPCOUNT
        movlw FRAME0LO          ; either the original frame ...
        btfss ECHOMODE,0
        movlw FRAMM0LO          ; ... or the modified frame will be shipped
        movwf FSR0              ; point FSR0 at start bit lsbyte
sts1
        movfw INDF0             ; get FRAMxxLO ...
        movlb 1                 ; bank 1 for PORTD
        movwf PORTD             ;              ... onto bus
        movlb 0                 ; bank 0 for PORTA
        incf FSR0,F             ; point to FRAMxxHI
        bcf RA3                 ; D8 at '823 is either a 0 ...
        btfsc INDF0,0
        bsf RA3                 ;                          ... or a 1
        bsf RA2                 ; apply rising edge to '823 CP and FIFO SI
        nop                     ; pulse width
        bcf RA2                 ; apply falling edge to '823 CP and FIFO SI
        incf FSR0,F             ; point to FRAMx[x+1]LO
        decfsz TMPCOUNT,F       ;
        goto sts1

        movlb 1                 ; bank 1 for PORTD
       ;clrf PORTD,F            ; unnecessary
        movlw 0xFF              ; configure PORTD bits as inputs
        movwf DDRD

        call busFEST            ; put front end status bits back onto bus


        tstfsz INTERCEPTFRAME   ; if the PC or OI is listening for a frame...
        goto sts2
        goto stsdone
sts2
        call frontEndStop
        call visibleDelay

        tstfsz ECHOMODE         ; depending on ECHOMODE, ...
        goto stsechopc
        btfss INTERCEPTFRAME,0  ; PC sets bit 0. OI sets bit 1
        goto stsoim
        call sendFrammToPC      ; ... send both FRAME and FRAMM to PC...
        goto stspcdone
stsoim
        call sciShipFramm       ; send FRAMM to OI
        goto stspcdone

stsechopc
        btfss INTERCEPTFRAME,0  ; PC sets bit 0. OI sets bit 1
        goto stsoie
        call sendFrameToPC      ; ... or just FRAME
        goto stspcdone
stsoie
        call sciShipFrame       ; send FRAME to OI
        goto stspcdone
stspcdone
        clrf INTERCEPTFRAME,F
        call frontEndStart

stsdone

endm    ; of SHIPTOSLAVE macro
;----------------------------------------------------
fefifoHandler
;       Front End FIFO handler.
;       This version does as much processing as possible as the frame is
;       received from the front end rather than doing everything after the
;       whole frame has been stored.
;       Called from pollLoop when FIFO OR bit is detected.
;       On entry, fefifo status byte is in wreg and on the bus.


        movlb 0                 ; select bank 0

        btfss WREG,7            ; if FIFO bits FULL=1 ...
        goto fefifonotfull
        btfsc WREG,6            ;        ... and HF=0 ...
        goto fefifonotfull

        call frontEndStop       ; ... then FIFO has overflowed
        movlw LED_FIFOOVERFLOW
        call ledFlash
        call visibleDelay
        call frontEndStart      ; reset the front end
        goto fefifohdone

fefifonotfull

        andlw 0x01              ; keep only the D8 bit
        movwf PUSHW             ; save

        movlw FRAME0HI          ; calc address of FRAMExHI where bit will go
        addwf FRAMECOUNT,W
        addwf FRAMECOUNT,W      ; do it twice, since HI/LO are interleaved
        movwf FSR0              ; points at FRAMExHI
        movfp PUSHW,INDF0       ; store D8 byte

                        ; get FIFO count byte
        bcf _138e               ; put bus in HiZ
        bsf _1381               ; pick bit 5 of '138
       ;bcf _1382               ; already clear
       ;bsf _1384               ; already set
        bsf _138e               ; drive FIFO count byte onto bus

        movlb 1
        nop                     ; wait   (hack: shouldn't have to)
       ;nop                     ; wait
       ;nop                     ; wait
       ;nop                     ; wait
        movfw PORTD             ; read bus into WREG
        movlb 0

        bcf _138e               ; bus HiZ
        bcf _1381               ; pick bit 4 of '138
        bsf _138e               ; restore FIFO status byte on bus

                        ; right now, FSR0 points to FRAMExHI and
                        ; WREG contains FRAMExLO.
                        ; Good time to check for a null count
        tstfsz WREG
        goto countnot0
        btfsc INDF0,0
        goto countnot0
        movlw LEDBIT_ZEROCOUNT  ; the 9-bit count is all 0's.
        call ledSet
        nop
        nop
        nop
        call ledClear
        SHIFTITOUT
        clrf FRAMECOUNT,0
        goto fefifohdone

countnot0
        decf FSR0,F             ; LO precedes HI. Consistent w/ PC
        movwf INDF0             ; save in FRAMExLO



        ; we want to call a separate routine for each FRAMECOUNT value
        ; Do a binary search for the right routine
        ; FC0 -- FC10 are macros defined for each value of FRAMECOUNT
        ; Each FCx macro ends with 'goto fefifohdone'

        btfsc FRAMECOUNT,0
        goto fcodd
fceven          ; framecount is EVEN: a pulse
        btfss FRAMECOUNT,1
        goto fc048
fc2610          ; framecount is 2, 6, or 10
        btfss FRAMECOUNT,3
        goto fc26
fc10            ; framecount is 10: 2nd stim pulse
       ;FCX
        FC10
fc26            ; framecount is 2 or 6
        btfss FRAMECOUNT,2
        goto fc2
fc6             ; framecount is 6: strength pulse
       ;FCX
        FC6
fc2             ;  framecount is 2: pair #
       ;FCX
        FC2
fc048           ; framecount is 0, 4, or 8
        tstfsz FRAMECOUNT
        goto fc48
fc0             ; framecount is 0: start bit
       ;FCX
        FC0
fc48    btfss FRAMECOUNT,3
        goto fc4
fc8             ; framecount is 8: 1st stim pulse
       ;FCX
        FC8
fc4             ; framecount is 4: mystery pulse
       ;FCX
        FC4


fcodd           ; framecount is ODD: a space
        movlw 1
        cpfseq FRAMECOUNT
        goto fc357911
fc1             ; framecount is 1: space following start bit
       ;FCX
        FC1
fc357911
        movlw 9
        cpfseq FRAMECOUNT
        goto fc35711
fc9             ; framecount is 9: space between 2 stim level pulses
       ;FCX
        FC9
fc35711         ; framecount is 3,5,7 or 11. Generic space.
        btfsc FRAMECOUNT,2
        goto fc57
fc311           ; framecount is 3 or 11
        btfsc FRAMECOUNT,3
        goto fc11
fc3             ; framecount is 3: space following electrode selection pulse
       ;FCX
        FC3
fc11            ; framecount is 11: inter-frame space
       ;FCX
        FC11
fc57            ; framecount is 5 or 7. Generic space.
        btfsc FRAMECOUNT,1
        goto fc7
fc5             ; framecount is 5: space following unknown pulse
       ;FCX
        FC5
fc7             ; framecount is 7: space following stim strength pulse
       ;FCX
        FC7

fefifohdone

        return          ; from fefifoHandler
;----------------------------------------------------
