
;    master.asm
;
;    PIC17C44 source for master controller of Spectra Post Processor
;
;     Pin assignments:
;       RA0  NC
;       RA1  NC
;       RA2  Downstream Buffer control CP/SI     OUT
;       RA3  Downstream buffer control D8        OUT
;       RA4  SCI from OIuC    RX                 IN
;       RA5  SCI   to OIuC    TX                 OUT
;
;       RB0  PC    bus handshake                 IN
;       RB1  Slave bus handshake                 IN
;       RB2  NC                                  OUT
;       RB3  NC                                  OUT
;       RB4  '138 enable                         OUT
;       RB5  '138 address lsb                    OUT
;       RB6  '138 address                        OUT
;       RB7  '138 address msb                    OUT
;
;       RC0  PC    bus handshake                 OUT
;       RC1  Slave bus handshake                 OUT
;       RC2  NC                                  OUT
;       RC3  Status LED enable                   OUT
;       RC4  Front End Control: LPARM            OUT
;       RC5  Front End Control: MR               OUT
;       RC6  Front End Control: SO               OUT
;       RC7  Front End Control: FERIN            OUT
;
;       RD0  Parallel Bus                        IO
;        .      .      .                          .
;       RD7  Parallel Bus                        IO
;
;       RE0  Status LEDs lsb                     OUT
;       RE1  Status LEDs                         OUT
;       RE2  Status LEDs msb                     OUT
;
;    '138 pin assignments:
;       0   OI    uC MCLR^
;       1   Slave uC MCLR^
;       2   NC
;       3   NC
;       4   Front End status
;       5   Front End Counts
;       6   PC interface
;       7   NC

        LIST p=17C44, n=1000

#include "P17CXX.INC"
#include "goodies.inc"
#include "master.inc"
#include "shared.inc"


        org 0x00                ; reset vector
        goto startup

;       org 0x20                ; interrupt vector
;       goto interruptPoll

;------------------------------------------------------------
startup
        bsf CPUSTA,GLINTD       ; CPUSTA[4]   disable interrupts

        bsf ALUSTA,FS0          ; ALUSTA[4-7] no auto-__crement
        bsf ALUSTA,FS1
        bsf ALUSTA,FS2
        bsf ALUSTA,FS3

                ; initialize port A
        movlb 0                 ; select bank 0
        clrf PORTA,F            ; write 0's
                                ;  (bits 2,3 are downstream buffer control)

                ; initialize port B
        movlb 0                 ; select bank 0
        clrf PORTB,F
        movlw 0x03              ; bits 0-1 are inputs
        movwf DDRB

                ; initialize port C
        movlb 1                 ; select bank 1
        movlw 0x00              ; all bits are outputs
        movwf DDRC
        clrf PORTC,F
        bsf PORTC,7             ; all but FERI bit low

                ; initialize port D    (parallel bus)
        movlb 1                 ; select bank 1
        movlw 0xFF              ; bits start out as inputs
        movwf DDRD
        clrf PORTD,F

                ; initialize port E    (status LEDs)
        movlb 1                 ; select bank 1
        movlw 0x00              ; bits are outputs
        movwf DDRE
        clrf PORTE,F

stimlab1                ; reset the front end
        call frontEndStop

       ;        ; boot the OI uC
       ;movlb 0                 ; select bank 0
       ;bcf _1381               ; pick bit 0 of '138
       ;bcf _1382
       ;bcf _1384
       ;bsf _138e               ;  enable the '138   OI uC MCLR^ falls
       ;nop                     ; wait
       ;bcf _138e               ; disable the '138   OI uC MCLR^ rises

       ;        ; boot the slave uC
       ;movlb 0                 ; select bank 0
       ;bsf _1381               ; pick bit 1 of '138
       ;bcf _1382
       ;bcf _1384
       ;bsf _138e               ;  enable the '138   slave uC MCLR^ falls
       ;nop                     ; wait
       ;bcf _138e               ; disable the '138   slave uC MCLR^ rises

                ; clear all file registers
        movlb 0                 ; select bank 0
        movlw 0x1A
        movwf FSR0
memloop0
        clrf INDF0,F
        incfsz FSR0,F
        goto memloop0

        movlr 1                 ; select GFR bank 1     >>>>17C43,4 only<<<<
        movlw 0x20
        movwf FSR0
memloop1
        clrf INDF0,F
        incfsz FSR0,F
        goto memloop1
        movlr 0                 ; select GFR bank 0

                ; initialize the SCI for communication w/ OI uC
        movlb 0                 ; select bank 0  twix unnecessary ??
        movlw 0x7
        movwf SPBRG             ; 19.x Kbaud at 10 MHz
        bcf TXSTA,4             ; asynchronous mode
        bcf TXSTA,5             ; transmit disabled
        bcf TXSTA,6             ; 8-bit data
       ;bcf TXSTA,7             ; don't care for asynch
        bsf RCSTA,4             ; enable reception
       ;bcf RCSTA,5             ; don't care for asynch
        bcf RCSTA,6             ; 8-bit data
        bsf RCSTA,7             ; Configure RA4,5 as serial IO pins
        movlb 1                 ; select bank 1 for PIE register
        bcf PIE,0               ; disable receive_buffer_full interrupt
        bcf PIE,1               ; disable transmit_buffer_empty interrupt

                ; ripple the status LEDs
        movlb 1                 ; bank 1 for port C,E access
        movlw 8
        movwf TMP
ledripple
        decf TMP,F
        movfw TMP
        call ledSet
        call visibleDelay
        call ledClear
        tstfsz TMP
        goto ledripple


        movlw 1
        movwf ECHOMODE          ; start off in echo mode

                        ; initialize the map values
        call resetMap


        movlw 0x81
        call ledFlash
       ;movlw 0x42
       ;call ledFlash

                ; boot the OI uC
        movlb 0                 ; select bank 0
        bcf _138e
        bcf _1381               ; pick bit 0 of '138
        bcf _1382
        bcf _1384
        bsf _138e               ;  enable the '138   OI uC MCLR^ falls
        call visibleDelay       ; wait
        bcf _138e               ; disable the '138   OI uC MCLR^ rises

                ; boot the slave uC
        movlb 0                 ; select bank 0
        bsf _1381               ; pick bit 1 of '138
        bcf _1382
        bcf _1384
        bsf _138e               ;  enable the '138   slave uC MCLR^ falls
        nop                     ; wait
        bcf _138e               ; disable the '138   slave uC MCLR^ rises

       ;movlw 0x24
       ;call ledFlash
        movlw 0x18
        call ledFlash

                        ; reset SCI reception
        movlb 0                 ; access to RCSTA & RCREG regs
        bcf RCSTA,4             ; reset OERR by clearing CREN
        bsf RCSTA,4             ; set CREN to re-enable reception
        movfw RCREG             ; read and discard. Clears FERR bit.

        movlw LED_BOOT_M2O
        call sciSendByte        ; send query to OI
        call sciReadByte        ; get response from OI
        sublw LED_BOOT_O2M
        btfsc ALUSTA,Z          ; OI's response should be LED_BOOT_O2M
        goto o2mok
        movlw LED_BADO2M        ; if not, flash error
        call ledFlash
o2mok
        movlw LED_BOOT_M2S
        call busSendSlaveByte   ; send a query to slave
        call busReadSlaveByte   ; get response from slave
       ;call ledFlash           ; flash it
        sublw LED_BOOT_S2M
        btfsc ALUSTA,Z          ; slave's response should be LED_BOOT_S2M
        goto s2mok
        movlw LED_BADS2M        ; if not, flash error
        call ledFlash
s2mok


        call frontEndStop       ; stop it again since a frame has probably accumulated
        call frontEndStart      ; enable the front end

        call busFEST    ; put FIFO status bits on bus before entering pollLoop
        goto pollLoop
                        ; end of startup
;-------------------------------------------------------
interruptPoll
;       Interrupt service routine
;       Interrupt service routine. Skeleton taken from 17C4x data book p 2.849
;       Interrupts disabled for entire program. Should not get executed.

                ; start of ISR skeleton preamble
ip0
        bsf CPUSTA,GLINTD       ; disable interrupts
        btfss CPUSTA,GLINTD
        goto ip0                ; make goddam sure that GLINTD gets set


        movpf WREG,TEMP_W
        movpf ALUSTA,TEMP_STATUS
        movpf BSR,TEMP_BSR
                ; end of ISR skeleton preamble

        nop                     ; ISR goes here.

                ; start of ISR skeleton appendix
        movfp TEMP_W,WREG
        movfp TEMP_STATUS,ALUSTA
        movfp TEMP_BSR,BSR
                ; end of ISR skeleton appendix

        retfie          ; end of interruptPoll
;-------------------------------------------------------
frontEndStart
;       rehash of pin assignments:
;               RC4  Front End Control: LPARM            OUT
;               RC5  Front End Control: MR               OUT
;               RC6  Front End Control: SO               OUT
;               RC7  Front End Control: FERIN            OUT

        movlb 1                 ; select bank 1
        bcf PORTC,4             ; LPARM=0
        bcf PORTC,6             ; SO=0
                ; for now, we'll leave FERIN high, and grab just 1 frame
        bcf PORTC,7             ; FERIN=0     << inhibit FIFOEN reset
        nop
        bsf PORTC,4             ; LPARM rising edge
        nop                     ; wait
        bcf PORTC,4             ; LPARM = 0

        return          ; from frontEndStart
;------------------------------------------------------
frontEndStop
;       rehash of pin assignments:
;               RC4  Front End Control: LPARM            OUT
;               RC5  Front End Control: MR               OUT
;               RC6  Front End Control: SO               OUT
;               RC7  Front End Control: FERIN            OUT
;
;       '138 bit 4 selects front end status byte, which is:
;               7: FULL
;               6: HF
;               5: IR
;               4: OR
;               0: D8

        movlb 1                 ; select bank 1
        bcf PORTC,4             ; LPARM=0
        bcf PORTC,6             ; SO=0
        bsf PORTC,7             ; FERI=1
        bsf PORTC,5             ; assert MR^

        call visibleDelay       ; wait  0.5     (These calls don't affect
        call visibleDelay       ; wait  1        the movlb status)
        call visibleDelay       ; wait
        call visibleDelay       ; wait  2
        call visibleDelay       ; wait
        call visibleDelay       ; wait  3
        call visibleDelay       ; wait
        call visibleDelay       ; wait  4 mS

        bcf PORTC,5             ; un-assert MR^

                ; check the front end FIFO status bits
        movlb 0                 ; select bank 0
        bcf _138e
        bcf _1381               ; pick bit 4 of '138
        bcf _1382
        bsf _1384
        bsf _138e               ; enable the '138   FIFO status bits on bus
        nop                     ; wait
                                ; PORTD is input by default
        movlb 1                 ; select bank 1
        movfw PORTD             ; get FIFO status byte into wreg
        movlb 0                 ; select bank 0
       ;bcf _138e               ; disable the '138 (no. leave status bits on bus)
        bcf wreg,0              ; clear D8 bit

        btfss wreg,5            ; IR bit should be on
        goto badferstir
        btfsc wreg,4            ; OR bit should be off
        goto badferstor
        btfsc wreg,7            ; FULL bit should be clear
        goto badferstfull
        goto fesdone
badferstir
        movlw LED_BADFERSTIR
       ;call ledFlash           ; display error code
        goto fesdone
badferstor
        movlw LED_BADFERSTOR
       ;call ledFastFlash       ; display error code
        goto fesdone
badferstfull
        movlw LED_BADFERSTFULL
        call ledFastFlash       ; display error code
       ;goto fesdone

fesdone
        return          ; from frontEndStop
;------------------------------------------------------
pollLoop
;               This is the main polling routine

                        ; get front end status byte, which is already on the
                        ; bus since busFEST has been called.
        movlb 1
        movfw PORTD
        btfss WREG,4            ; test the FIFO OR status bit
        goto plchkoi
        call fefifoHandler
                        ; for performance, poll the other stuff only
        goto plend      ; when front end is fully serviced

plchkoi                 ; check whether there's been an SCI request from OI uC
        movlb 1                 ; PIR in bank 1
        btfss PIR,0             ; RCIF bit will be set if a byte has come in
        goto plchkpc
        call busHiZ
        call sciHandler
        call busFEST

plchkpc                 ; check whether the PC wants to talk
        movlb 0                 ; bank 0 for PORTB
        btfss PORTB,0
        goto plchkslave
        call busHiZ
        call hshPC              ; HandShake Handler for PC
        call busFEST

plchkslave              ; check whether the slave wants to talk
        btfss PORTB,1
        goto plend
        call busHiZ
        call hshSlave           ; HandShake Handler for slave
        call busFEST

plend
        goto pollLoop
;----------------------------------------------------
busFEST
;       put front end status bits on bus

        movlb 0                 ; select bank 0
        bcf _138e
        bcf _1381               ; pick bit 4 of '138
        bcf _1382
        bsf _1384
        bsf _138e               ; drive FIFO status bits onto bus
       ;nop
        return          ; from busFEST
;----------------------------------------------------
busHiZ
;       put bus in HiZ state

        movlb 0                 ; select bank 0
        bcf _138e               ; put bus in HiZ state
       ;nop
        return          ; from busHiZ
;----------------------------------------------------
DIVLUT  macro num,denom
;       This macro takes 2 8-bit values, divides them using the
;       2 divLut tables and returns an 8-bit result in wreg.
;       Both arguments are assumed to be file register names.
;       It is assumed (but not checked) that denom>num, so that the
;       returned result is a scaled fraction: 0=0, 255=1.
;       Algorithm is:   DH = divLutH(denom)
;                       DL = divLutL(denom)
;                       num*DH --> A:B   (in PRODH:L)
;                       num*DL --> C:D
;                       result = B+C      Pretty darned good approximation.
;       Memory locations used:  DLB

        movfw denom
        call divLutH            ; returns LUT value in wreg
        mulwf num               ; PRODH:L contains A:B
        movpf PRODL,DLB

        movfw denom
        call divLutL            ; returns LUT value in wreg
        mulwf num               ; PRODH:L contains C:D
        movpf PRODH,WREG

        addwf DLB,W             ; wreg = B+C

        endm            ; end of DIVLUT macro
;------------------------------------------------------
#include fefifoh.asm
;------------------------------------------------------
;       The Map Conversion Data values are derived from the POTVALS and are
;        used to convert a frame from FRAME to FRAMM.
;       They have the following memory image:
;       MC_VOL          Volume control.
;       MC_SHIFT        # of electrodes to shift. Added to PAIRNO. -3 to +3
;       MC_Q            Q-factor. 0-6: 6=loud 3=same 0=soft
;       MC_T0           Threshold Levels
;       ...                "
;       MC_T19             "
;       MC_R0           Ranges: C-T
;       ...                "
;       MC_R19             "
;       When the APPLY button is hit, the OI uC sends raw POT readings.
;       The regenMCData routine takes them, along with the current value
;       of the Map, and re-generates these MC_xx values.
;
;       Note: The MC_T's and the MC_R's are pre-compensated for the
;             minwidth adder, meaning that MC_Tx can be less than
;             MINSTIMLVL.

;------------------------------------------------------
regenMCData
;       This routine converts the raw POT readings from the OI uC
;        and the current Map values and builds the MC_xx values.
;       It is normally called after sciresp_potvals has updated the
;       POTVALSxx array from SCI.

        movlr 1                 ; POTVALs are in bank1
        movfw POTVAL23          ; POT 23 is SHIFT amount
        movlr 0                 ; restore bank 0
        andlw 0xE0              ; take 3 msb's
        rlncf WREG,W            ; rotate to msb's to lsb spots
        rlncf WREG,W
        rlncf WREG,W            ; WREG is 0 to 7
        btfsc WREG,2            ; if WREG is 4 to  7 ...
        decf WREG,F             ;  ... make it 3 to 6
                                ; WREG is 0 to 6
        decf WREG,W
        decf WREG,W
        decf WREG,W
        movwf MC_SHIFT          ; result is between -3 and 3

        movlr 1                 ; POTVALs are in bank1
        movfw POTVAL22          ; POT 22 is Q-factor
        movlr 0                 ; restore bank 0
        andlw 0xE0              ; take 3 msb's
        rlncf WREG,W            ; rotate msb's to lsb spots
        rlncf WREG,W
        rlncf WREG,W
        movwf MC_Q              ; 0-7
        movlw 4
        cpfslt MC_Q
        decf MC_Q,F             ; result is 0-6

                                ; since POT22 is hooked up 'backwards', we
        movfw MC_Q              ; prefer to deal with 6-this_number
        sublw 6
        movwf MC_Q              ; MC_Q = 6 - MC_Q


                        ; POTVAL21 will be used for volume control
        movlr 1                 ; POTVALs are in bank1
        movfw POTVAL21          ; POT 21 is volume control
        movlr 0                 ; restore bank 0
        movwf MC_VOL            ; no bit manipulations needed.


                        ; The MC_T's will either be a copy of the MAPT's
                        ; or the values sent from the PC,
                        ; depending on the value of OVERRIDETHRESHOLDS:
                        ; 0=no overrides
                        ; 1=use actual threshold values from PC
                        ; 2=use threshold adders from OI
        tstfsz OVERRIDETHRESHOLDS
        goto rmcd_otnot0
                                ; no threshold overrides:
        movlw MAPT0             ; copy threshold values from MAPTx to MC_Tx
        movwf FSR0
        movlw MC_T0
        movwf FSR1
        movlw .20
        movwf TEMPLEN
rmcd1
        movfw INDF0
        movwf INDF1
        incf FSR0,F
        incf FSR1,F
        decfsz TEMPLEN,F
        goto rmcd1
        goto rmcd_tdone


rmcd_otnot0
        btfss OVERRIDETHRESHOLDS,0
        goto rmcd_otnot1
                                ; OVERRIDETHRESHOLDS=1:
                                ; set MC_T's = to values from PC
        movlw PCMAPT0           ; careful: PCMAPTx is in bank 1
        movwf FSR0
        movlw MC_T0
        movwf FSR1
        movlw .20
        movwf TEMPLEN
rmcd_ot1
        movlr 1         ; swap to bank 1
        movfw INDF0
        movlr 0         ; swap to bank 0
        movwf INDF1
        incf FSR0,F
        incf FSR1,F
        decfsz TEMPLEN,F
        goto rmcd_ot1
        goto rmcd_tdone


rmcd_otnot1
        btfss OVERRIDETHRESHOLDS,1
        goto rmcd_otnot2
                                ; OVERRIDETHRESHOLDS=2:
                                ; use threshold adders from OI
        movlw TOIADDER0         ; careful: TOIADDERx is in bank 1
        movwf FSR0
        movlw MC_T0
        movwf FSR1
        movlw .20
        movwf TEMPLEN
rmcd_ot2
        movlw MAPT0
        addwf TEMPLEN,0
        decf WREG,0
        movwf FSR0      ; points to MAPTx, starting with MAPT19

        movlw MC_T0
        addwf TEMPLEN,0
        decf WREG,0
        movwf FSR1      ; points to MC_Tx, starting with MC_T19
        movpf INDF0,INDF1       ; MC_Tx=MAPTx

        tstfsz INDF0    ; if MAPTx=0 ...
        goto rmcd_ot2a
        goto rmcd_ot2z
rmcd_ot2a               ; MAPT was non-0

        movlw TOIADDER0
        addwf TEMPLEN,0
        decf WREG,0
        movwf FSR0      ; points to TOIADDERx in bank 1
        movlr 1         ; swap to bank 1
        movfw INDF0
        movlr 0         ; swap to bank 0
        movwf TMP       ; TMP=TOIADDERx
        addwf INDF1,0   ; wreg=MAPTx+TOIADDERx
        movwf PUSHW     ; PUSHW=MAPTx+TOIADDERx= desired result normally

                        ; sanity check: if adder was positive & result<orig
                        ;  then we overflowed thru 256
        btfsc TMP,7
        goto passes_sc0
                        ; adder was positive
        movfw PUSHW
        cpfsgt INDF1    ; INDF1 points to MC_Tx, which contains MAPTx
        goto passes_sc0
        movlw LED_TOIADDEROF    ; fails overflow sanity check
        call ledFlash
        clrf INDF1,F            ; set MC_Tx=0
        goto rmcd_ot2z
passes_sc0
                        ; sanity check: if adder was negative & result>orig
                        ;  then we underflowed thru 0
        btfss TMP,7
        goto passes_sc1
                        ; adder was negative
        movfw PUSHW
        cpfslt INDF1    ; INDF1 points to MC_Tx, which contains MAPTx
        goto passes_sc1
        movlw LED_TOIADDERUF    ; fails underflow sanity check
        call ledFlash
        clrf INDF1,F            ; set MC_Tx=0
        goto rmcd_ot2z
passes_sc1
                        ; passes sanity check. put sum in MC_Tx
        movfp PUSHW,INDF1

rmcd_ot2z
        decfsz TEMPLEN,F        ; move on to [x-1]'th pair
        goto rmcd_ot2


        goto rmcd_tdone
rmcd_otnot2
        movlw LED_BADOT         ; bad value for OVERRIDETHRESHOLDS
        call ledFlash
rmcd_tdone                      ; MC_T's are done


                ; Now calculate new MC_Range values.
#if 0
                        ; Original method just allows for decreasing range.
                        ; MC_R[x] = MAPR[x] * POTVAL[x+1]
        movlw .20
        movwf POTSTOGO
rmcd2
        movlw MAPR0
        addwf POTSTOGO,W
        decf WREG,W
        movwf FSR0              ; INDF0 points to MAPR[x]

        movlw POTVAL0
        addwf POTSTOGO,W
        movwf FSR1              ; INDF1 points to POTVAL[x+1]
        movlr 1                 ; POTVALs are in bank1
        movfw INDF1             ; wreg contains POTVAL[x+1]
        movlr 0                 ; restore bank0

        mulwf INDF0             ; PRODH:L contains MAPR[x] * POTVAL[x+1]

        movlw MC_R0
        addwf POTSTOGO,W
        decf WREG,W
        movwf FSR0              ; INDF0 points to MC_R[x]

        movpf PRODH,INDF0       ; move scaled range into MC_R[x]

        decfsz POTSTOGO,F
        goto rmcd2
#else
                ; New method implements threshold overrides and volume control
                ; MC_R[x] = ((MAPC[x] - MC_T[x]) * POTVAL[x+1])*volumefactor
                ;  where volumefactor is a little tricky:
                ;
                ; volume control scales between 0 & VMAX where VMAX < 2.
                ; The calculation is: RANGE = RANGE*2*POTVAL * VMAX/2
                ; The RANGE*2*POTVAL is implemented as:
                ;  Multiply RANGE 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.33, then VRATIO=255*1.33/2=169=xA9
                ;  and a POTVAL of 255/1.33=192=xC0 on the volume control pot
                ;  gives 'identity' volume when the electrode pots are at max.
                ; Equivalently, if each electrode's pot is at xC0 and the
                ;  volume control pot is at max, we get an identity transform.
#define VRATIO 0xA9

        movlw .20
        movwf POTSTOGO
rmcd2
        movlw MAPC0
        addwf POTSTOGO,W
        decf WREG,W
        movwf FSR0              ; INDF0 points to MAPC[x]

        movlw MC_T0
        addwf POTSTOGO,W
        decf WREG,W
        movwf FSR1              ; INDF1 points to MC_T[x]

        movfw INDF1             ; WREG = MC_T[x]
        cpfsgt INDF0                    ; make sure MAPC>MC_T
        goto rmcd3
        subwf INDF0,W           ; WREG = MAPC[x] - MC_T[x]
        movwf TMP               ; save aside

        movlw POTVAL0
        addwf POTSTOGO,W
        movwf FSR1              ; INDF1 points to POTVAL[x+1]
        movlr 1                 ; POTVALs are in bank1
        movfw INDF1             ; wreg contains POTVAL[x+1]
        movlr 0                 ; restore bank0

        mulwf TMP               ; PRODH:L is (MAPC[x]-MC_T[x]) * POTVAL[x+1]
                                ; if we were not doing a volume control,
                                ; we'd move PRODH into MC_R[x]

        movfw PRODH
        mulwf MC_VOL            ; jump thru hoops to do volume adjustment
        rlcf PRODH,W            ;  as described above
        bcf WREG,0
        btfsc PRODL,7
        bsf WREG,0
        movwf TMP
        movlw VRATIO            ; multiply by VRATIO
        mulwf TMP               ; PRODH contains volume-adjusted range

        movlw MC_R0
        addwf POTSTOGO,W
        decf WREG,W
        movwf FSR0              ; INDF0 points to MC_R[x]

        movpf PRODH,INDF0       ; move scaled range into MC_R[x]
        goto rmcd4

rmcd3                   ; MAPC<MC_T  Set values to 0
        movlw MC_R0
        addwf POTSTOGO,W
        decf WREG,W
        movwf FSR0
        clrf INDF0,F            ; MC_R[x]=0

        movlw MC_T0
        addwf POTSTOGO,W
        decf WREG,W
        movwf FSR0
        clrf INDF0,F            ; MC_T[x]=0

rmcd4
        decfsz POTSTOGO,F
        goto rmcd2
#endif

#define GANGEDTHRESH
#ifdef GANGEDTHRESH
                ; POTVAL0 will be treated as a ganged threshold adjustment
                ; We'll do an adjustment as:
                ; if (MC_Tx)
                ;  {gangedAdder=hibyte(POTVAL0*MC_Rx)
                ;   MC_Tx+=gangedAdder
                ;   MC_Rx-=gangedAdder
                ;  }
        movlw .20
        movwf POTSTOGO
rmcdga0
        movlw MC_T0
        addwf POTSTOGO,W
        decf WREG,W
        movwf FSR0              ; INDF0 points to MC_T[x]
        tstfsz INDF0
        goto rmcdga1
        goto rmcdgaend

rmcdga1                 ; MC_T[x] is non-0
        movlw MC_R0
        addwf POTSTOGO,W
        decf WREG,W
        movwf FSR1              ; INDF1 points to MC_R[x]

        movlr 1                 ; POTVALs are in bank1
        movfw POTVAL0           ; get POTVAL0, the ganged threshold control
        movlr 0                 ; restore bank0

        mulwf INDF1             ; PRODH = MC_R[x] * POTVAL0 = gangedAdder

        movfw PRODH
        addwf INDF0,F           ; MC_T[x] += gangedAdder

        movfw PRODH
        subwf INDF1,F           ; MC_R[x] -= gangedAdder

rmcdgaend
        decfsz POTSTOGO,F
        goto rmcdga0

#endif
                                ; Sometimes, when I press APPLY, I hear a
                                ; beep, and find that MAPT0 is set to some
                                ; really low value. Maybe this will fix it.
                                ; It doesn't, but leave it in anyway.
        clrf FRAMECOUNT,F

        return          ; from regenMCData
;-----------------------------------------------------
qFactor
;       This routine adjusts for the Q-factor setting.
;       On entry, wreg contains a stim level fraction scaled as: 0=T, 255=C.
;       On exit, wreg contains the adjusted stimulus level.
;       MC_Q is known to be between 0 and 6, based on POTVAL22.
;        Q=6 --> loudest  Q=3 --> no adjustment  Q=0 --> softest
;       If we define:    stimout = stimin + qadj,
;        then we can use a symmetry property to require only 3 LUTs.
;            qadj(stimin,Q)=-qadj(255-stimin,6-Q)
;
;shared variable: MC_Q
;local variables: QSTIMSAVE
;                 QSYM
;                 QSTIM
;                 QVALUE
;                 QADJ

        movwf QSTIMSAVE         ; save value
        movlw 3
        cpfseq MC_Q
        goto qf0
        movfw QSTIMSAVE         ; MC_Q=3. No adjustment necessary.
        goto qfdone
qf0
                                ; save some values
        clrf QSYM,F             ; 0 flag says we're not using symmetry
        movfw QSTIMSAVE
        movwf QSTIM             ; work version of stimulus level
        movfw MC_Q
        movwf QVALUE            ; MC_Q 4-6

        movlw 3
        cpfsgt MC_Q
        goto qf1
        goto qf2
qf1
                        ; MC_Q is 0-2. Use the symmetry property.
        bsf QSYM,0              ; non-0 flag says we're using symmetry
        movfw QSTIMSAVE
        sublw 0xFF
        movwf QSTIM             ; QSTIM=255-QSTIMSAV
        movfw MC_Q
        sublw 6
        movwf QVALUE            ; QVALUE=6-MC_Q

qf2                     ; QVALUE is 4-6. Make adjustment
        movlw 4
        cpfseq QVALUE
        goto qf3
                        ; QVALUE is 4. Use qlut4
        movfw QSTIM
        call qLut4
        movwf QADJ
        goto qf5
qf3
        movlw 5
        cpfseq QVALUE
        goto qf4
                        ; QVALUE is 5. Use qlut5
        movfw QSTIM
        call qLut5
        movwf QADJ
        goto qf5
qf4
                        ; QVALUE is 6. Use qlut6
        movfw QSTIM
        call qLut6
        movwf QADJ

qf5                     ; QADJ has been calc'd
        tstfsz QSYM     ; test whether we're using symmetry
        goto qfsym
        goto qfnosym
qfsym
        movfw QADJ
        subwf QSTIMSAVE,W       ; wreg=QSTIMSAVE-QADJ
        goto qfdone
qfnosym
        movfw QADJ
        addwf QSTIMSAVE,W       ; wreg=QSTIMSAVE+QADJ

qfdone

        return          ; from qFactor
;------------------------------------------------------------------
createDummyFramm
;       This routine fills FRAMM with numbers matching a dummy frame.
;       The DFRAMExxx are all #define'd constants.
        movlw DFRAME0LO
        movwf  FRAMM0LO
        movlw DFRAME1LO
        movwf  FRAMM1LO
        movlw DFRAME2LO
        movwf  FRAMM2LO
        movlw DFRAME3LO
        movwf  FRAMM3LO
        movlw DFRAME4LO
        movwf  FRAMM4LO
        movlw DFRAME5LO
        movwf  FRAMM5LO
        movlw DFRAME6LO
        movwf  FRAMM6LO
        movlw DFRAME7LO
        movwf  FRAMM7LO
        movlw DFRAME8LO
        movwf  FRAMM8LO
        movlw DFRAME9LO
        movwf  FRAMM9LO
        movlw DFRAME10LO
        movwf  FRAMM10LO
        movlw DFRAME11LO
        movwf  FRAMM11LO

        movlw DFRAME0HI
        movwf  FRAMM0HI
        movlw DFRAME1HI
        movwf  FRAMM1HI
        movlw DFRAME2HI
        movwf  FRAMM2HI
        movlw DFRAME3HI
        movwf  FRAMM3HI
        movlw DFRAME4HI
        movwf  FRAMM4HI
        movlw DFRAME5HI
        movwf  FRAMM5HI
        movlw DFRAME6HI
        movwf  FRAMM6HI
        movlw DFRAME7HI
        movwf  FRAMM7HI
        movlw DFRAME8HI
        movwf  FRAMM8HI
        movlw DFRAME9HI
        movwf  FRAMM9HI
        movlw DFRAME10HI
        movwf  FRAMM10HI
        movlw DFRAME11HI
        movwf  FRAMM11HI
        return          ; from createDummyFramm
;------------------------------------------------------------------
resetMap
;       This routine resets the map T & C values to 0
;       Stored in 2 arrays of 20 values each at MAPT0 and MAPC0
;       could speed this up by assuming T,C,R arrays are contiguous

        movlw MAPT0             ; ptr to 0th Threshold value
        movwf FSR0
        movlw .20                ; 20 electrode values
tloop
        clrf INDF0,F
        incf FSR0,F
        decfsz wreg,W
        goto tloop

        movlw MAPC0             ; ptr to 0th Comfort value
        movwf FSR0
        movlw .20                ; 20 electrode values
cloop
        clrf INDF0,F
        incf FSR0,F
        decfsz wreg,W
        goto cloop

        movlw MAPR0             ; ptr to 0th Range value
        movwf FSR0
        movlw .20                ; 20 electrode values
rloop
        clrf INDF0,F
        incf FSR0,F
        decfsz wreg,W
        goto rloop

        return          ; from resetMap
;------------------------------------------------------------------
sciSendByte
;       This routine sends a byte to the OI uC thru SCI.
;       On entry, the value to be sent is in WREG.

        movlb 0                 ; access to TXREG
        movwf TXREG             ; load data to be sent
        bsf TXSTA,5             ; enable transmission

        movlw LEDBIT_SCISENDBYTE
        call ledSet

                        ; wait till transmission is done
                        ; (TXIF bit (PIR[1]) will be set)
        movlb 1         ; access to PIR
scis1
        btfss PIR,1     ; test TXIF bit
        goto scis1

        call ledClear

        return          ; from sciSendByte
;--------------------------------------------------------
sciReadByte
;       This routine waits for and reads a byte from the SCI port
;       The byte read is returned in WREG. (and in SRBRECDBYTE)
;       On error, WREG is 0.


                        ; wait for a byte to come in
        movlw LEDBIT_SCIREADBYTE
        call ledSet
        movlb 1                 ; PIR in bank 1
srb1
        btfss PIR,0             ; RCIF bit will be set if a byte has come in
        goto srb1

        call ledClear

        movlb 0                 ; access to RCSTA & RCREG regs

        btfss RCSTA,2           ; check FERR bit for framing error
        goto srbnoferr
        movfw RCREG             ; read and discard. Clears FERR bit.
        movlw LED_SCIFERR
        call ledFlash
        clrf SRBRECDBYTE,F
srbnoferr
        movfw RCREG
        movwf SRBRECDBYTE       ; save the byte we received

        btfss RCSTA,1           ; check OERR bit for overrun
        goto srbnooverrun
        bcf RCSTA,4             ; reset OERR by clearing CREN
        bsf RCSTA,4             ; set CREN to re-enable reception
        clrf SRBRECDBYTE,F
srbnooverrun
                        ; error-free byte from OI uC is saved in SRBRECDBYTE
        movfw SRBRECDBYTE        ; redundant when no error

        return          ; from sciReadByte
;--------------------------------------------------------
sciHandler
;       Handler to respond to an SCI request from OI uC
;       This routine is called from the main polling loop when SCIREQUEST
;       is detected non-0: OI uC wants to talk.
;       The protocol used for talking with the OI uC is:
;               * Initiator sends 1 byte and waits for a reply.
;                    Content is interpreted by responder
;               * Responder's reply consists of
;                    1 byte which tells how many MORE bytes are to follow
;                    n bytes
;               * I'll worry about contention later.

        call frontEndStop

        call sciReadByte        ; reads byte, returns it in wreg
                                ; and in SRBRECDBYTE

                ; SRBRECDBYTE should be one of SCICMD_*
                ; Decide which it is and respond to it.
        movlw SCICMD_DUMMY      ; if it's a DUMMY request ...
        cpfseq SRBRECDBYTE
        goto scih1
        call sciresp_dummy      ; ... call the DUMMY handler
        goto scih_complete
scih1
        movlw SCICMD_POTVALS    ; if it's a POTVALS request ...
        cpfseq SRBRECDBYTE
        goto scih2
        call sciresp_potvals    ; ... call the POTVALS handler
        goto scih_complete
scih2
        movlw SCICMD_SETECHO    ; if it's a SETECHO request ...
        cpfseq SRBRECDBYTE
        goto scih3
        call sciresp_setecho    ; ... call the SETECHO handler
        goto scih_complete
scih3
        movlw SCICMD_SETMOD     ; if it's a SETMOD request ...
        cpfseq SRBRECDBYTE
        goto scih4
        call sciresp_setmod     ; ... call the SETMOD  handler
        goto scih_complete
scih4
        movlw SCICMD_APPLY      ; if it's an APPLY request ...
        cpfseq SRBRECDBYTE
        goto scih5
        call sciresp_apply      ; ... call the APPLY handler
        goto scih_complete
scih5
        movlw SCICMD_SHIPMAP    ; if it's a SHIPMAP request ...
        cpfseq SRBRECDBYTE
        goto scih6
        call sciresp_shipmap    ; ... call the SHIPMAP handler
        goto scih_complete
scih6
        movlw SCICMD_SHIPMC     ; if it's a SHIPMC request ...
        cpfseq SRBRECDBYTE
        goto scih7
        call sciresp_shipMC     ; ... call the SHIPMC handler
        goto scih_complete
scih7
        movlw SCICMD_SHIPFRAME  ; if it's a SHIPFRAME request ...
        cpfseq SRBRECDBYTE
        goto scih8
        call sciresp_shipFrame  ; ... call the SHIPFRAME handler
        goto scih_complete
scih8
        movlw SCICMD_RESETMAP   ; if it's a RESETMAP request ...
        cpfseq SRBRECDBYTE
        goto scih9
        call sciresp_resetmap    ; ... call the RESETMAP handler
        goto scih_complete
scih9
        movlw SCICMD_APPTHRESHOV ; if it's an APPTHRESHOV request ...
        cpfseq SRBRECDBYTE
        goto sciha
        call sciresp_appthreshov ; ... call the APPTHRESHOV handler
        goto scih_complete
sciha
        movlw SCICMD_DISTHRESHOV ; if it's an DISTHRESHOV request ...
        cpfseq SRBRECDBYTE
        goto scihb
        call sciresp_disthreshov ; ... call the DISTHRESHOV handler
        goto scih_complete
scihb

                ; these SCI_SLAVE* commands should be #define'd
                ; with the same values as their PCREQ_SLAVE* equivalents
                ; since the slave doesn't care whether the command
                ; originated from the PC or the OI, and the master
                ; handles them as a dumb echoing middleman
        movlw SCI_SLAVESENDBEEPPARMS
        cpfseq SRBRECDBYTE
        goto scih_s0
        call sci_msecho         ; send to echo middleman
        goto scih_complete
scih_s0
        movlw SCI_SLAVETOGGLEBEEP
        cpfseq SRBRECDBYTE
        goto scih_s1
        call sci_msecho         ; send to echo middleman
        goto scih_complete
scih_s1


        call sciresp_error       ; None of the above. Reply with ERROR

scih_complete

        call frontEndStart

        movlb 0                 ; leave with bank 0 enabled
        return          ; from sciHandler
;------------------------------------------------------
sciresp_resetmap

        call resetMap

        movlw 0                 ; send ACK
        call sciSendByte

        return          ; from sciresetmap
;------------------------------------------------------
sciresp_disthreshov
;       this routine handles a SCICMD_DISTHRESHOV request.
;       so we will DISable THRESHold OVerrides
;       The OI is telling us to go back to copying the MAPT's to the MC_T's
;       Similar to the PCREQ_DISTHRESHOV command from the PC

        clrf OVERRIDETHRESHOLDS,F

        movlw 0
        call sciSendByte        ; send an ACK

        call regenMCData        ; virtually press APPLY button.
                                ; (Does not resend POT values)

        return          ; from sciresp_disthreshov
;----------------------------------------------------------
sciresp_appthreshov
;       this routine handles a SCICMD_APPTHRESHOV request.
;       so we will APPly THRESHold OVerrides
;       The OI wants to send us threshold adder values to go into the MC_T's
;       Similar to the PCREQ_APPTHRESHOV command from the PC, except that
;       adders, rather than actual thresholds are sent

        movlw 0
        call sciSendByte        ; tell OI to proceed with sending values

        movlw TOIADDER0
        movwf FSR0              ; INDF0 points to TOIADDER0 in bank 1

        call sciReadByte        ; get # of bytes to follow
        movwf BYTESTOGO
scr_ato0
        call sciReadByte        ; receive individual threshold adder values
        movlr 1
        movwf INDF0             ; store in TOIADDERx in bank 1
        movlr 0
        incf FSR0,F

        decfsz BYTESTOGO,F
        goto scr_ato0

        movlw 0
        call sciSendByte        ; send an ACK

                                ; set flag so TOIADDERx's will be used
        movlw 0x02              ; instead of PCMAPTx's in regenMCData
        movwf OVERRIDETHRESHOLDS

        call regenMCData        ; virtually press APPLY button.
                                ; (Does not resend POT values)

        return          ; from sciresp_appthreshov
;----------------------------------------------------------
sciresp_apply
;       this routine is called when sciHandler gets a SCICMD_APPLY

        movlw 0                 ; OI is expecting an ACK
        call sciSendByte

       ;movlw LED_APPLY         ; twix do nothing but flash for now
       ;call ledFlash

        call regenMCData        ; convert pot readings to Map Conversion structure

        return          ; from sciresp_apply
;------------------------------------------------------
sciresp_setmod
;       this routine is called when sciHandler gets a SCICMD_SETMOD

        movlw 0                 ; # of bytes to be sent
        call sciSendByte        ; means OK

        clrf ECHOMODE,F         ; put the thing in modify mode

        return          ; from sciresp_setmod
;------------------------------------------------------
sciresp_setecho
;       this routine is called when sciHandler gets a SCICMD_SETECHO

        movlw 0                 ; # of bytes to be sent
        call sciSendByte        ; means OK

        movlw 1                 ; put the thing in echo mode
        movwf ECHOMODE

        return          ; from sciresp_setecho
;------------------------------------------------------
sciresp_error
;       This routine is called when sciHandler gets an un-recognized command
;       Flash an error code followed by the bad byte.

        movlw LED_SCIBADCMD
        call ledFlash
        movfw SRBRECDBYTE
        call ledFlash

        return          ; from sciresp_error
;------------------------------------------------------
sciresp_dummy
;       This routine responds to an SCICMD_DUMMY request

        movlw 2                 ; # of bytes to be sent
        call sciSendByte
        movlw 0x12              ; 1st byte
        call sciSendByte
        movlw 0x34              ; 2nd byte
        call sciSendByte

        return          ; from sciresp_dummy
;------------------------------------------------------
sciresp_potvals
;       This routine receives the raw pot values from the OI uC.
;       These raw values are massaged in the potsToMapConv routine.


        movlw 0                 ; # of add'l bytes to be sent.
        call sciSendByte        ; This tells the OI uC to go ahead and ship
                                ; the POT values.

        movlw POTVAL0           ; careful: POTVALS are in bank 1
        movwf FSR0
        call sciReadByte
        movwf POTSTOGO
        movlw .24
        cpfseq POTSTOGO         ; should be 24
        goto srpvnot24
srpv24
        call sciReadByte        ; read potvalx from sci
        movlr 1
        movwf INDF0             ; save it in POTVALx in bank 1
        movlr 0
        incf FSR0,F             ; point to next potval
        decfsz POTSTOGO,F       ; decrement 'to-go' number
        goto srpv24

        movlw 0
        call sciSendByte        ; send ACK

        goto srpvdone
srpvnot24
        movfw LED_SRPVNOT24
        call ledFlash
        goto srpv24
srpvdone

        return          ; from sciresp_potvals
;-------------------------------------------------------
sciresp_shipmap
; this routine gets called in response to an SCICMD_SHIPMAP request from the OI
; It sends the current values of MAP's Threshold, Comfort and Range

        movlw .60
        call sciSendByte        ; send # of bytes in message

        movlw MAPT0
        movwf FSR0
        movlw .20               ; 20 threshold values to follow
        movwf TMP
srsmt
        movfw INDF0
        call sciSendByte        ; send threshold values
        incf FSR0,F
        decfsz TMP,F
        goto srsmt

        movlw MAPC0
        movwf FSR0
        movlw .20               ; 20 comfort values to follow
        movwf TMP
srsmc
        movfw INDF0
        call sciSendByte        ; send comfort values
        incf FSR0,F
        decfsz TMP,F
        goto srsmc

        movlw MAPR0
        movwf FSR0
        movlw .20               ; 20 range values to follow
        movwf TMP
srsmr
        movfw INDF0
        call sciSendByte        ; send range values
        incf FSR0,F
        decfsz TMP,F
        goto srsmr

        call sciReadByte        ; get ACK ...
        tstfsz WREG             ;     ... and check that ACK was 0
        goto srsmbadack
        goto srsmend
srsmbadack
        movlw LED_SRSMBADACK
        call ledFlash

srsmend
        return          ; from sciresp_shipmap
;-------------------------------------------------------
sciresp_shipMC
; this routine gets called in response to an SCICMD_SHIPMC request from the OI
; It sends the current values of MC's Threshold, Comfort and Range

        movlw .60
        call sciSendByte        ; send # of bytes in message

        movlw MC_T0
        movwf FSR0
        movlw .20               ; 20 threshold values to follow
        movwf TMP
srsmct
        movfw INDF0
        call sciSendByte        ; send threshold values
        incf FSR0,F
        decfsz TMP,F
        goto srsmct

        movlw MC_T0             ; there is no MC_C array, so calculate it.
        movwf FSR0
        movlw MC_R0
        movwf FSR1
        movlw .20               ; 20 comfort values to follow
        movwf TMP
srsmcc
        movfw INDF0
        addwf INDF1,W           ; MC_C = MC_T + MC_R
        call sciSendByte        ; send comfort values
        incf FSR0,F
        incf FSR1,F
        decfsz TMP,F
        goto srsmcc

        movlw MC_R0
        movwf FSR0
        movlw .20               ; 20 range values to follow
        movwf TMP
srsmcr
        movfw INDF0
        call sciSendByte        ; send range values
        incf FSR0,F
        decfsz TMP,F
        goto srsmcr

        call sciReadByte        ; get ACK ...
        tstfsz WREG             ;     ... and check that ACK was 0
        goto srsmcbadack
        goto srsmcend
srsmcbadack
        movlw LED_SRSMBADACK
        call ledFlash

srsmcend
        return          ; from sciresp_shipMC
;-------------------------------------------------------
sciShipFrame
;       This routine ships the contents of FRAME in response to a prior
;       SCICMD_SHIPFRAME request from the OI

        movlw SCICMD_FRAMEDATA
        call sciSendByte

        call sciReadByte        ; OI responds with go-ahead
                                ; skip data check

        movlw FRAME0LO
        movwf FSR0
        movlw .22                ; .22 values to follow
        movwf TMP
        call sciSendByte
ssfe
        movfw INDF0
        call sciSendByte        ; send threshold values
        incf FSR0,F
        decfsz TMP,F
        goto ssfe

        call sciReadByte        ; OI sends ACK
                                ; skip ACK==0 check

        return          ; from sciShipFrame
;-------------------------------------------------------
sciShipFramm
;       This routine ships the contents of FRAMM in response to a prior
;       SCICMD_SHIPFRAME request from the OI

        movlw SCICMD_FRAMEDATA
        call sciSendByte

        call sciReadByte        ; OI responds with go-ahead
                                ; skip data check

        movlw FRAMM0LO
        movwf FSR0
        movlw .22                ; .22 values to follow
        movwf TMP
        call sciSendByte
ssfm
        movfw INDF0
        call sciSendByte        ; send threshold values
        incf FSR0,F
        decfsz TMP,F
        goto ssfm

        call sciReadByte        ; OI sends ACK
                                ; skip ACK==0 check

        return          ; from sciShipFramm
;-------------------------------------------------------
sciresp_shipFrame
;       This routine gets called in response to an SCICMD_SHIPFRAME request
;       from the OI. It registers a request for the current frame.
;       The actual frame will be sent when it is received.

        bsf INTERCEPTFRAME,1
        movlw 0
        call sciSendByte        ; send an ACK
        return          ; from sciresp_shipFrame
;-------------------------------------------------------
hshPC
;       HandShake Handler for PC
;       This routine is called from the main polling loop when RB0
;       is detected high, indicating that the PC wants to talk.
;       The PC initiates ALL such conversations.

        call frontEndStop

        clrf INTERCEPTFRAME,F   ; if the getframe hung, we'd get a bus snafu

        call busReadPCByte

        movwf PCREQBYTE         ; save received byte

                ; PCREQBYTE should be one of PCREQ_*
                ; Decide which it is and respond to it.
        movlw PCREQ_ECHO        ; if it's a ECHO request ...
        cpfseq PCREQBYTE
        goto hshpc1
        call pcresp_echo        ; ... call the ECHO handler
        goto pcreq_complete
hshpc1
        movlw PCREQ_DUMMY       ; if it's a DUMMY request ...
        cpfseq PCREQBYTE
        goto hshpc2
        call pcresp_dummy       ; ... call the DUMMY handler
        goto pcreq_complete
hshpc2
        movlw PCREQ_GETFRAME    ; if it's a GETFRAME request ...
        cpfseq PCREQBYTE
        goto hshpc3
        call pcresp_getFrame    ; ... call the GETFRAME handler
        goto pcreq_complete
hshpc3
        movlw PCREQ_GETMAP      ; if it's a GETMAP request ...
        cpfseq PCREQBYTE
        goto hshpc4
        call pcresp_getMap      ; ... call the GETMAP handler
        goto pcreq_complete
hshpc4
        movlw PCREQ_RESETMAP    ; if it's a RESETMAP request ...
        cpfseq PCREQBYTE
        goto hshpc5
        call pcresp_resetMap    ; ... call the RESETMAP handler
        goto pcreq_complete
hshpc5
        movlw PCREQ_TOGGLEECHO  ; if it's a TOGGLEECHO request ...
        cpfseq PCREQBYTE
        goto hshpc6
        call pcresp_toggleEcho  ; ... call the TOGGLEECHO handler
        goto pcreq_complete
hshpc6
        movlw PCREQ_QUERYECHO   ; if it's a QUERYECHO request ...
        cpfseq PCREQBYTE
        goto hshpc7
        call pcresp_queryEcho   ; ... call the QUERYECHO handler
        goto pcreq_complete
hshpc7
        movlw PCREQ_QUERYMC     ; if it's a QUERYMC request ...
        cpfseq PCREQBYTE
        goto hshpc8
        call pcresp_queryMC     ; ... call the QUERYMC handler
        goto pcreq_complete
hshpc8
        movlw PCREQ_GETFRAMEFRAG; if it's a GETFRAMEFRAG request ...
        cpfseq PCREQBYTE
        goto hshpc9
        call pcresp_getFrameFrag; ... call the GETFRAMEFRAG handler
        goto pcreq_complete
hshpc9
        movlw PCREQ_GENERIC     ; if it's a GENERIC request ...
        cpfseq PCREQBYTE
        goto hshpc10
        call pcresp_generic     ; ... call the GENERIC handler
        goto pcreq_complete
hshpc10
        movlw PCREQ_APPTHRESHOV         ; if it's a APPTHRESHOV request ...
        cpfseq PCREQBYTE
        goto hshpc11
        call pcresp_appthreshov ; ... call the APPTHRESHOV handler
        goto pcreq_complete
hshpc11
        movlw PCREQ_DISTHRESHOV         ; if it's a DISTHRESHOV request ...
        cpfseq PCREQBYTE
        goto hshpc12
        call pcresp_disthreshov ; ... call the DISTHRESHOV handler
        goto pcreq_complete
hshpc12

        movlw PCREQ_SLAVEDUMMY  ; if it's a SLAVEDUMMY request ...
        cpfseq PCREQBYTE
        goto hshpc101
        call pcresp_msecho      ; all slave commands go thru msecho routine
        goto pcreq_complete
hshpc101
        movlw PCREQ_SLAVEINTERCEPT      ; if it's a SLAVEINTERCEPT request ...
        cpfseq PCREQBYTE
        goto hshpc102
        call pcresp_msecho      ; all slave commands go thru msecho routine
        goto pcreq_complete
hshpc102
        movlw PCREQ_SLAVESHIPFRAME      ; if it's a SLAVESHIPFRAME request ...
        cpfseq PCREQBYTE
        goto hshpc103
        call pcresp_msecho      ; all slave commands go thru msecho routine
        goto pcreq_complete
hshpc103
        movlw PCREQ_SLAVESHIPFRAMB      ; if it's a SLAVESHIPFRAMB request ...
        cpfseq PCREQBYTE
        goto hshpc104
        call pcresp_msecho      ; all slave commands go thru msecho routine
        goto pcreq_complete
hshpc104
        movlw PCREQ_SLAVETOGGLESSC      ; if it's a SLAVETOGGLESSC request ...
        cpfseq PCREQBYTE
        goto hshpc105
        call pcresp_msecho      ; all slave commands go thru msecho routine
        goto pcreq_complete
hshpc105
        movlw PCREQ_SLAVETOGGLETP       ; if it's a SLAVETOGGLETP  request ...
        cpfseq PCREQBYTE
        goto hshpc106
        call pcresp_msecho      ; all slave commands go thru msecho routine
        goto pcreq_complete
hshpc106
        movlw PCREQ_SLAVETOGGLEBEEP     ; if it's a SLAVETOGGLEBEEP request ...
        cpfseq PCREQBYTE
        goto hshpc107
        call pcresp_msecho      ; all slave commands go thru msecho routine
        goto pcreq_complete
hshpc107
        movlw PCREQ_SLAVESENDBEEPPARMS  ; if it's a SLAVESENDBEEPPARMS request ...
        cpfseq PCREQBYTE
        goto hshpc108
        call pcresp_msecho      ; all slave commands go thru msecho routine
        goto pcreq_complete
hshpc108

        movlw PCREQ_OIGETPOTVALS; if it's an OIGETPOTVALS request ...
        cpfseq PCREQBYTE
        goto hshpc201
        call pcresp_oigetpotvals        ; get pot vals from OI and send to PC
        goto pcreq_complete
hshpc201

        call pcresp_error       ; None of the above. Reply with ERROR

pcreq_complete

        call frontEndStart      ; restart the front end

        movlb 0                 ; leave with bank 0 enabled
        return          ; from hshPC
;----------------------------------------------------
busReadPCByte
;       This routine waits for a byte on the PC bus and handshakes it into wreg

                ; enable the bus driver on the PCif board
        movlb 0                 ; select bank 0
        bcf _138e
        bcf _1381               ; pick bit 6 of '138
        bsf _1382
        bsf _1384
        bsf _138e               ; enable OE^ on PCif board

        movlw LEDBIT_BRBWAIT0
        call ledSet
        movlb 0
brpb0
        btfss PORTB,0           ; wait for RB0 REQ to be asserted
        goto brpb0
        call ledClear
        movlb 1
        bsf PORTC,0             ; assert RC0 ACK

        movlw LEDBIT_BRBWAIT1
        call ledSet
        movlb 0
brpb1
        btfsc PORTB,0           ; wait for RB0 REQ to fall
        goto brpb1
        call ledClear
                        ; valid data is on the bus. grab it
        movlb 1
        movfw PORTD
        movwf BYTEFROMBUS
        bcf PORTC,0             ; assert RC0 ACK low

        movlb 0                 ; select bank 0
        bcf _138e               ; disable OE^ on PCif board
                                ; twix we'd normally want to put fifo status byte onto bus

                        ; wreg contains the byte we just read
        return          ; from busReadPCByte
;--------------------------------------------------------
busSendPCByte
;       This routine sends a byte to the printer port using bus handshake.
;       On entry, the value to be sent is in WREG.

        movwf BUSSENDTMP        ; save the byte till it's needed
        movlb 1
        bsf RC0                 ; assert RC0 REQ
        movlw LEDBIT_BUSREQ
        call ledSet
                        ; wait till RB0 ACK is detected
        movlb 0
bsbpc1
        btfss RB0
        goto bsbpc1
                ; ACK on RB0 has been detected. PC is listening.
        movlb 1
        clrf DDRD,F             ; configure PORTD bits as outputs
        movfw BUSSENDTMP
        movwf PORTD             ; put data on bus
        movlw LEDBIT_BUSDATA
        call ledSet

        movlb 1
        bcf RC0                 ; de-assert RC0 REQ
                        ; wait for PC to respond with RB0 ACK low
        movlb 0
bsbpc2
        btfsc RB0
        goto bsbpc2
                        ; transfer is complete.

        call ledClear

        movlb 1
        movlw 0xFF
        movwf DDRD              ; configure PORTD bits as inputs

        movlb 0
        return          ; from busSendPCByte
;-------------------------------------------------------
pcresp_dummy
;       This routine responds to the PC's PCREQ_DUMMY message
;       Response is for debug only. No relevant info returned.

        call visibleDelay
        movlw 2                 ; 2 bytes to follow
        call busSendPCByte
        movlw 0x00              ; 1st
        call busSendPCByte
        movlw 0x74              ; 2nd
        call busSendPCByte

        call busReadPCByte        ; get ACK byte into wreg
        tstfsz wreg             ; make sure ACK is 0x00
        goto prdbadack
        goto prddone
prdbadack
        movlw LED_PRDBADACK
        call ledFlash
prddone

        return          ; from pcresp_dummy
;-------------------------------------------------------
pcresp_oigetpotvals
;       this routine responds to a PCREQ_OIGETPOTVALS request
;       ugly. shouldn't need to nearly duplicate potvals code.

        movlw SCICMD_POTVALS
        call sciSendByte        ; ask the OI to send POTVALS

        movlw POTVAL0           ; careful: POTVALS are in bank 1
        movwf FSR0
        call sciReadByte
        movwf POTSTOGO
        movlw .24
        cpfseq POTSTOGO         ; should be 24
        goto ogpvnot24
ogpv24
        call sciReadByte        ; read potvalx from sci
        movlr 1
        movwf INDF0             ; save it in POTVALx in bank 1
        movlr 0
        incf FSR0,F             ; point to next potval
        decfsz POTSTOGO,F       ; decrement 'to-go' number
        goto ogpv24

        movlw 0
        call sciSendByte        ; send ACK to OI

                        ; now send POTVALs to PC
        call visibleDelay
        movlw .24               ; # of POTVALs to be sent to PC
        movwf TEMPLEN           ; save in temp var
        call busSendPCByte

        movlw POTVAL0
        movwf FSR0              ; indirect addressing points to POTVALs
ogpv1
        movlr 1
        movfw INDF0             ; bank 1
        movlr 0
        call busSendPCByte
        incf FSR0,F             ; could do this automagically
        decfsz TEMPLEN,F
        goto ogpv1

        call busReadPCByte      ; get ACK byte into wreg
        tstfsz wreg             ; make sure ACK is 0x00
        goto ogpvbadack
        goto ogpvdone
ogpvbadack
        movlw LED_OGPVBADACK
        call ledFlash

        goto ogpvdone
ogpvnot24
        movfw LED_OGPVNOT24
        call ledFlash
        goto ogpv24
ogpvdone
        return          ; from pcresp_oigetpotvals
;-------------------------------------------------------
pcresp_echo
;       This routine responds to the PC's PCREQ_ECHO message by
;       sending back the byte in the message.
;       Length protocol is not observed.
;       For debug only.

        call busReadPCByte      ; read the byte
        call visibleDelay
        call visibleDelay
        call visibleDelay
        call visibleDelay
        call busSendPCByte      ; send it back

        return          ; from pcresp_echo
;-------------------------------------------------------
pcresp_error
;       This routine is called when an unknown request comes from the PC.

        movlw 0                 ; 0 bytes to follow
        call busSendPCByte
        return          ; of pcresp_error
;----------------------------------------------------------
pcresp_getFrame
        bsf INTERCEPTFRAME,0    ; flag is caught in shipFrame
        call frontEndStart
        return          ; from pcresp_getFrame
;----------------------------------------------------------
pcresp_generic
        bsf INTERCEPTEVENT,0    ; flag is caught when some registered event occurs
        call frontEndStart
        return          ; from pcresp_generic
;----------------------------------------------------------
pcresp_disthreshov
;       this routine handles a PCREQ_DISTHRESHOV request.
;       so we will DISable THRESHold OVerrides
;       The PC is telling us to go back to copying the MAPT's to the MC_T's

        clrf OVERRIDETHRESHOLDS,F

        call visibleDelay
        call visibleDelay
        call visibleDelay
        movlw 0
        call busSendPCByte      ; send an ACK

        call regenMCData        ; virtually press APPLY button.
                                ; (Does not resend POT values)

        return          ; from pcresp_disthreshov
;----------------------------------------------------------
pcresp_appthreshov
;       this routine handles a PCREQ_APPTHRESHOV request.
;       so we will APPly THRESHold OVerrides
;       The PC wants to send us threshold values to go into the MC_T's

        call visibleDelay
        call visibleDelay
        call visibleDelay
        movlw 0
        call busSendPCByte      ; tell PC to proceed with sending values

        movlw PCMAPT0
        movwf FSR0              ; INDF0 points to PCMAPT0 in bank 1

        call busReadPCByte      ; get # of bytes to follow
        movwf BYTESTOGO
pcr_ato0
        call busReadPCByte      ; receive individual threshold values
        movlr 1
        movwf INDF0             ; store in PCMAPTx in bank 1
        movlr 0
        incf FSR0,F

        decfsz BYTESTOGO,F
        goto pcr_ato0

        call visibleDelay
        call visibleDelay
        call visibleDelay
        movlw 0
        call busSendPCByte      ; send an ACK

        movlw 0x01              ; set flag so PCMAPTx's will get used
        movwf OVERRIDETHRESHOLDS

        call regenMCData        ; virtually press APPLY button.
                                ; (Does not resend POT values)

        return          ; from pcresp_appthreshov
;----------------------------------------------------------
pcresp_getFrameFrag

        call visibleDelay
        call visibleDelay
        movlw .25
        call busSendPCByte

        movfw FRAMECOUNT        ; send framecount...
        call busSendPCByte

        movlw FRAME0LO          ; followed by whatever's in frame buffer
        movwf FSR0
        movlw .24
        movwf BYTESTOGO
gffloop
        movfw INDF0
        call busSendPCByte
        incf FSR0,F
        decfsz BYTESTOGO,F
        goto gffloop

        call busReadPCByte      ; get ACK byte into wreg
        tstfsz wreg             ; make sure ACK is 0x00
        goto gffbadack
        goto gffdone
gffbadack
        movlw LED_GFFBADACK
        call ledFlash
gffdone
        return          ; from pcresp_getFrameFrag
;----------------------------------------------------------
pcresp_toggleEcho
        call visibleDelay
        movlw 0
        call busSendPCByte      ; reply w/ ACK
        movlw 1
        tstfsz ECHOMODE         ; toggle ECHOMODE
        clrf ECHOMODE,W
        movwf ECHOMODE
        return          ; from pcresp_toggleEcho
;----------------------------------------------------------
pcresp_queryEcho
        call visibleDelay
        movfw ECHOMODE
        call busSendPCByte      ; reply w/ ECHOMODE value. No ACK.
        return          ; from pcresp_queryEcho
;----------------------------------------------------------
pcresp_queryMC
;       This routine is called when the PC sends a PCREQ_QUERYMC command.
;       The PC is sent a whole pile of stuff related to map conversion.

        call visibleDelay

        movlw .106
        call busSendPCByte      ; # of bytes to expect

                        ; send 24 POTVALS
        movlw POTVAL0
        movwf FSR0
        movlw .24
        movwf TEMPLEN
qmc0
        movlr 1                ; POTVALs are in bank1
        movfw INDF0            ; get POTVALx into wreg
        movlr 0                ; restore bank0
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto qmc0

        movfw MC_SHIFT
        call busSendPCByte
        movfw MC_Q
        call busSendPCByte

                        ; send 20 MC_T's
        movlw MC_T0
        movwf FSR0
        movlw .20
        movwf TEMPLEN
qmc1
        movfw INDF0            ; get MC_T[x] into wreg
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto qmc1

                        ; send 20 MC_R's
        movlw MC_R0
        movwf FSR0
        movlw .20
        movwf TEMPLEN
qmc2
        movfw INDF0            ; get MC_R[x] into wreg
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto qmc2

                        ; send 20 MAPT's
        movlw MAPT0
        movwf FSR0
        movlw .20
        movwf TEMPLEN
qmc3
        movfw INDF0            ; get MAPT[x] into wreg
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto qmc3

                        ; send 20 MAPR's
        movlw MAPR0
        movwf FSR0
        movlw .20
        movwf TEMPLEN
qmc4
        movfw INDF0            ; get MAPR[x] into wreg
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto qmc4

        call busReadPCByte      ; get ACK byte into wreg
        tstfsz wreg             ; make sure ACK is 0x00
        goto qmcbadack
        goto qmcdone
qmcbadack
        movlw LED_QMCBADACK
        call ledFlash
qmcdone

        return          ; from pcresp_queryMC
;----------------------------------------------------------
sendFrameToPC
;       This routine is called when the PC sends a PCREQ_GETFRAME command.
;       The PC is sent the latest contents of FIFOFRAME.

        call busHiZ             ; put bus into HiZ
        call visibleDelay
        movlw .24               ; # of bytes to be transferred from the frame
        movwf TEMPLEN           ; save in temp var
        call busSendPCByte

        movlw FRAME0LO
        movwf FSR0              ; indirect addressing points to FIFOFRAME
pcrgf1
        movfw INDF0
        call busSendPCByte
        incf FSR0,F             ; could do this automagically
        decfsz TEMPLEN,F
        goto pcrgf1

        call busReadPCByte        ; get ACK byte into wreg
        tstfsz wreg             ; make sure ACK is 0x00
        goto pcrgfbadack
        goto pcrgfdone
pcrgfbadack
        movlw LED_PCRGFBADACK
        call ledFlash
pcrgfdone
        call busFEST            ; put FE status byte on bus

        return          ; from sendFrameToPC
;------------------------------------------------------------------
sendFrammToPC
;       This routine is called when the PC sends a PCREQ_GETFRAME command.
;       The PC is sent the latest contents of FIFOFRAME and it's modified
;       equivalent. Like sendFrameToPC, except that 2 frames are sent.

        call busHiZ             ; put bus into HiZ
        call visibleDelay
        movlw .156              ; total # of bytes to be sent
        addlw .9                ; temporary extras for debug
        call busSendPCByte

        movlw .24               ; # of bytes to be transferred from FRAME
        movwf TEMPLEN           ; save in temp var
        movlw FRAME0LO
        movwf FSR0              ; indirect addressing points to FRAME
pcrgfm1
        movfw INDF0
        call busSendPCByte
        incf FSR0,F             ; could do this automagically
        decfsz TEMPLEN,F
        goto pcrgfm1

        movlw .24               ; # of bytes to be transferred from FRAMM
        movwf TEMPLEN           ; save in temp var
        movlw FRAMM0LO
        movwf FSR0              ; indirect addressing points to FRAMM
pcrgfm2
        movfw INDF0
        call busSendPCByte
        incf FSR0,F             ; could do this automagically
        decfsz TEMPLEN,F
        goto pcrgfm2

                        ; send 24 POTVALS
        movlw POTVAL0
        movwf FSR0
        movlw .24
        movwf TEMPLEN
pcrgfm3
        movlr 1                ; POTVALs are in bank1
        movfw INDF0            ; get POTVALx into wreg
        movlr 0                ; restore bank0
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto pcrgfm3

        movfw PAIRNO
        call busSendPCByte
        movfw NEWPAIRNO
        call busSendPCByte
        movfw MC_SHIFT
        call busSendPCByte
        movfw MC_Q
        call busSendPCByte

                        ; send 20 MC_T's
        movlw MC_T0
        movwf FSR0
        movlw .20
        movwf TEMPLEN
pcrgfm4
        movfw INDF0            ; get MC_T[x] into wreg
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto pcrgfm4

                        ; send 20 MC_R's
        movlw MC_R0
        movwf FSR0
        movlw .20
        movwf TEMPLEN
pcrgfm5
        movfw INDF0            ; get MC_R[x] into wreg
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto pcrgfm5

                        ; send 20 MAPT's
        movlw MAPT0
        movwf FSR0
        movlw .20
        movwf TEMPLEN
pcrgfm6
        movfw INDF0            ; get MAPT[x] into wreg
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto pcrgfm6

                        ; send 20 MAPR's
        movlw MAPR0
        movwf FSR0
        movlw .20
        movwf TEMPLEN
pcrgfm7
        movfw INDF0            ; get MAPR[x] into wreg
        call busSendPCByte
        incf FSR0,F
        decfsz TEMPLEN,F
        goto pcrgfm7

                        ; start of temporary extras for debug
        movfw NEWRANGE
        call busSendPCByte
        movfw NEWTHRESH
        call busSendPCByte
        movfw STIMLVL
        call busSendPCByte
        movfw NEWSTIMLVL
        call busSendPCByte
        movfw NEWADDER
        call busSendPCByte
        movfw NUMERATOR
        call busSendPCByte
        movfw DENOMINATOR
        call busSendPCByte
        ;movlr 1
         movfw WBEFOREQ         ; debug
        ;movlr 0
        call busSendPCByte
        ;movlr 1
         movfw WAFTERQ          ; debug
        ;movlr 0
        call busSendPCByte
                        ; end of temporary extras for debug

        call busReadPCByte      ; get ACK byte into wreg
        tstfsz wreg             ; make sure ACK is 0x00
        goto pcrgfmbadack
        goto pcrgfmdone
pcrgfmbadack
        movlw LED_PCRGFBADACK
        call ledFlash
pcrgfmdone
        call busFEST            ; put FE status byte on bus

        return          ; from sendFrammToPC
;------------------------------------------------------------------
pcresp_getMap
;       This routine is called when the PC sends a PCREQ_GETMAP command.
;       The PC is sent the current MAPT and MAPC arrays

        call visibleDelay
        movlw .40               ; # of bytes to be transferred from the frame
        call busSendPCByte

        movlw .20               ; 20 T values
        movwf TEMPLEN
        movlw MAPT0
        movwf FSR0              ; indirect addressing points to MAPT array
pcrgm1
        movfw INDF0
        call busSendPCByte
        incf FSR0,F             ; could do this automagically
        decfsz TEMPLEN,F
        goto pcrgm1

        movlw .20               ; 20 C values
        movwf TEMPLEN
        movlw MAPC0
        movwf FSR0              ; indirect addressing points to MAPC array
pcrgm2
        movfw INDF0
        call busSendPCByte
        incf FSR0,F             ; could do this automagically
        decfsz TEMPLEN,F
        goto pcrgm2

        call busReadPCByte        ; get ACK byte into wreg
        tstfsz wreg             ; make sure ACK is 0x00
        goto pcrgmbadack
        goto pcrgmdone
pcrgmbadack
        movlw LED_PCRGMBADACK
        call ledFlash
pcrgmdone

        return          ; from pcresp_getMap
;------------------------------------------------------------------
pcresp_resetMap
;       This routine is called when the PC sends a PCREQ_RESETMAP command.
;       The PC is sent the current MAPT and MAPC arrays

        call resetMap
        call visibleDelay
        movlw 0                 ; send ACK
        call busSendPCByte
        return          ; from pcresp_resetMap
;------------------------------------------------------------------
pcresp_msecho
;       This routine is called when the PC sends a PCREQ_SLAVE* command.
;       The actual command byte is in wreg. It's not important exactly
;       which one it is, as the master acts only as a middleman
;       between the PC and the slave, and this routine services all such
;       PC-to-slave conversations.

        movwf MSECHOCMD         ; save for posterity
        call busSendSlaveByte   ; pass the msg on to the slave

msechostart
        call visibleDelay       ; so PC won't miss low state
        call busReadSlaveByte   ; echo slave's response to PC
        movwf BYTESTOGO
        call busSendPCByte

        tstfsz BYTESTOGO
        goto s2m1
        goto msechodone         ; 0-length message. Final sentence.
s2m1
        call busReadSlaveByte   ; echo each byte from slave to PC
        call busSendPCByte
        decfsz BYTESTOGO,F
        goto s2m1
                        ; done echoing slave's response to PC.
        call busReadPCByte      ; echo PC's response to slave
        movwf BYTESTOGO
        call busSendSlaveByte

        tstfsz BYTESTOGO
        goto m2s1
        goto msechodone         ; 0-length message. Final sentence.
m2s1
        call busReadPCByte      ; echo each byte from PC to slave
        call busSendSlaveByte
        decfsz BYTESTOGO,F
        goto m2s1
                        ; done echoing PC's response to slave.
        goto msechostart

msechodone

        movlb 0
        return          ; from pcresp_msecho
;-----------------------------------------------------
sci_msecho
;       This routine is similar to pcresp_msecho, except that the master
;       acts as a middleman between the slave and OI instead of slave and PC
;       The OI initiates all such conversations

        movwf MSECHOCMD         ; save for posterity
        call busSendSlaveByte   ; pass the msg on to the slave

omsechostart
        call visibleDelay
        call busReadSlaveByte   ; echo slave's response to OI
        movwf BYTESTOGO
        call sciSendByte

        tstfsz BYTESTOGO
        goto s2m2o
        goto omsechodone        ; 0-length message. Final sentence.
s2m2o
        call busReadSlaveByte   ; echo each byte from slave to OI
        call sciSendByte
        decfsz BYTESTOGO,F
        goto s2m2o
                        ; done echoing slave's response to OI.
        call sciReadByte        ; echo OI's response to slave
        movwf BYTESTOGO
        call busSendSlaveByte

        tstfsz BYTESTOGO
        goto o2m2s
        goto omsechodone        ; 0-length message. Final sentence.
o2m2s
        call sciReadByte        ; echo each byte from OI to slave
        call busSendSlaveByte
        decfsz BYTESTOGO,F
        goto o2m2s
                        ; done echoing OI's response to slave.
        goto omsechostart

omsechodone

        movlb 0
        return          ; from sci_msecho
;-----------------------------------------------------
hshSlave
;       HandShake Handler for Slave
;       This routine is called from the main polling loop when RB1
;       is detected high.
;       Since the master initiates ALL conversations with the slave,
;       and only in response to a query from the PC, the pollLoop should
;       never detect a slave REQ. Therefore, this routine just flashes
;       the LEDs with an error code.

        call frontEndStop
        movlw LED_SLAVEREQ
        call ledFlash
        call frontEndStart

        movlb 0
        return          ; from hshSlave
;------------------------------------------------------
busReadSlaveByte
;       This routine waits for a byte on the slave bus and handshakes
;       it into wreg. Identical to busReadPCByte but different handshake lines.

        movlw LEDBIT_BRBWAIT0
        call ledSet
        movlb 0
brsb0
        btfss RB1               ; wait for RB1 REQ to be asserted
        goto brsb0
        call ledClear
        movlb 1
        bsf RC1                 ; assert RC1 ACK

        movlw LEDBIT_BRBWAIT1
        call ledSet
        movlb 0
brsb1
        btfsc PORTB,1           ; wait for RB1 REQ to fall
        goto brsb1
        call ledClear
                        ; valid data is on the bus. grab it
        movlb 1
        movfw PORTD
        movwf BYTEFROMBUS
        bcf RC1                 ; assert RC1 ACK low
        movlb 0

                        ; wreg contains the byte we just read
        return          ; from busReadSlaveByte
;--------------------------------------------------------
busSendSlaveByte
;       This routine sends a byte to the slave using bus handshake.
;       On entry, the value to be sent is in WREG.
;       Just like busSendPCByte, but different handshake lines.

        movlb 0                 ; frontEndStop leaves status bits on bus
        bcf _138e               ; so ensure that bus is in hiZ

        movwf BUSSENDTMP        ; save the byte till it's needed
        movlb 1
        bsf RC1                 ; assert RC1 REQ
        movlw LEDBIT_BUSREQ
        call ledSet
                        ; wait till RB1 ACK is detected
        movlb 0
bsbs1
        btfss RB1
        goto bsbs1
                ; ACK on RB1 has been detected. Slave is listening.
        movlb 1
        clrf DDRD,F             ; configure PORTD bits as outputs
        movfw BUSSENDTMP
        movwf PORTD             ; put data on bus
        movlw LEDBIT_BUSDATA
        call ledSet
        movlb 1
        bcf RC1                 ; de-assert RC1 REQ
        movlb 0

                        ; wait for slave to respond with RB1 ACK low
bsbs2
        btfsc RB1
        goto bsbs2
                        ; transfer is complete.

        call ledClear

        movlb 1
        movlw 0xFF
        movwf DDRD              ; configure PORTD bits as inputs

        movlb 0
        return          ; from busSendSlaveByte
;------------------------------------------------------
;hshOI
;;       HandShake Handler for OI
;;       This routine is called from the main polling loop when RB2
;;       is detected high.
;;       This section of the bus is not even wired up!
;
;        nop
;        movlb 0                 ; leave with bank 0 enabled
;        return          ; from hshOI
;----------------------------------------------------
ledFastFlash
;       This routine does an ledFlash, but for a shorter duration
        movwf LEDBYTE           ; save wreg
        movlw 0x10
        movwf LEDDURATION
        movfw LEDBYTE           ; restore wreg
        call ledFlash           ; will last 1/16 as long as a normal ledFlash
        clrf LEDDURATION,F      ; restore so next ledFlash will be normal
        return          ; from ledFastFlash
;----------------------------------------------------
ledFlash
;               This routine flashes the status LEDs in a pattern
;               which matches the contents of wreg on entry.
;               Just 1 LED at a time is on, but we'll make them flicker
;               so that a combo is apparent.
;               Because there is no LED enable line, 0 can't be displayed.
;               Incoming bank is not saved. Bank 0 is active on return.

        movlb 1                 ; access to ports
        movwf LEDBYTE           ; save the byte we'll display

        movlw 0xFF
        movwf LEDLOOP0

        movfw LEDDURATION       ; 0 for a normal-length ledFlash
        movwf LEDLOOP1

        movlw 0x02              ; set duration of flash
        movwf LEDLOOP2          ; LEDLOOP2 = 0x0F --> 29 sec at 10 MHz
ledf2
        movfw LEDBYTE
        movwf LEDSHIFT          ; working copy of LEDBYTE
        movlw 8
        movwf LEDREGE           ; the value we'll write to PORTE for each bit
ledf1
        decf LEDREGE,F
        btfss LEDSHIFT,7        ; is msb set?
        goto ledf0              ; no. don't display

        movfw LEDREGE           ; msb is set. write LEDREGE to PORTE
        movwf PORTE
        bsf PORTC,3             ; enable status LEDs
        nop
        nop
        nop
        bcf PORTC,3             ; disable status LEDs
ledf0
        rlncf LEDSHIFT,1        ; shift left
        tstfsz LEDREGE
        goto ledf1


                                ; repeat enough times to be visible
        decfsz LEDLOOP0,F       ; innermost delay loop
        goto ledf2
        movlw 0xFF              ; reset loop
        movwf LEDLOOP0

        decfsz LEDLOOP1,F       ; middle delay loop
        goto ledf2
        movfw LEDDURATION       ; reset loop
        movwf LEDLOOP1

        decfsz LEDLOOP2,F       ; outermost delay loop
        goto ledf2

       ;clrf PORTE,F            ; set LEDs back to 0 before returning
        bcf RC3                 ; disable status LEDs

        movlb 0                 ; bank 0
        return          ; from ledFlash
;-------------------------------------------------------
ledSet
;       This routine turns on a status LED bit. WREG tells which one
        movlb 1                 ; bank 1 for access to PORTs
        movwf PORTE
        bsf RC3                 ; enable the LEDs
        movlb 0
        return          ; from ledSet
;-------------------------------------------------------
ledClear
;       This disables the status LEDs
        movlb 1                 ; bank 0 for access to PORTC
        bcf RC3                 ; disable the LEDs
        return          ; from ledClear
;-------------------------------------------------------
visibleDelay
;       This routine delays long enough to be visible
;       Takes about 1/2 mS at 16 MHz

        movwf VDTMP             ; save
        movlw 0xFF
vd1
        nop
        nop
        nop             ; add NOPs if you increase the clock speed
        nop
        nop
        decfsz wreg,W
        goto vd1

        movfw VDTMP             ; restore
        return          ; from visibleDelay
;-------------------------------------------------------
#include "divluts.inc"
#include "qluts.inc"
        end
