;    oi.asm
;
;    PIC16C74 source for OI controller of Spectra Post Processor
;
;     Pin assignments:
;       RA0  Analog input                        IN
;       RA1  Analog input                        IN
;       RA2  Button 1                            IN
;       RA3  Analog input                        IN
;       RA4  Button 2                            IN
;       RA5  Button 3                            IN
;
;       RB0  Shift indicator lsb                 OUT
;       RB1  Shift indicator                     OUT
;       RB2  Shift indicator msb                 OUT
;       RB3  Q-factor indicator lsb              OUT
;       RB4  Q-factor indicator                  OUT
;       RB5  Q-factor indicator msb              OUT
;       RB6  Mode select                         IN
;       RB7  Apply button                        IN
;
;       RC0  Analog MUX selects A                OUT
;       RC1  Analog MUX selects B                OUT
;       RC2  Analog MUX selects STX              OUT
;       RC3  Analog MUX selects STY              OUT
;       RC4  1-shot done                         OUT
;       RC5  Force echo                          OUT
;       RC6  SCI   to master uC  TX              OUT
;       RC7  SCI from master uC  RX              IN
;
;       RD0  Status LED enable (formerly RA2)    OUT
;       RD1  Optrex RS                           OUT
;       RD2  Optrex R/W                          OUT
;       RD3  Optrex Enable                       OUT
;       RD4  Optrex DB4                          I/O
;       RD5  Optrex DB5                          I/O
;       RD6  Optrex DB6                          I/O
;       RD7  Optrex DB7                          I/O
;
;       RE0  Status LEDs lsb                     OUT
;       RE1  Status LEDs                         OUT
;       RE2  Status LEDs msb                     OUT
;
;     MCLR^  From master uC '138 bit 0
;
;   Analog channel assignments:
;                                      A   B  STX STY
;     pot usage                  pin  RC0 RC1 RC2 RC3
;
;      0  Low Threshold          RA0   0   0   1   0   edge  top
;      1  Electrode  0 (low)     RA0   0   0   0   1   inner top
;      2  Electrode  1           RA0   1   0   1   0   edge
;      3  Electrode  2           RA0   1   0   0   1   inner
;      4  Electrode  3           RA0   0   1   1   0   edge
;      5  Electrode  4           RA0   0   1   0   1   inner
;      6  Electrode  5           RA0   1   1   1   0   edge
;      7  Electrode  6           RA0   1   1   0   1   inner
;
;      8  Electrode  7           RA1   0   0   1   0   edge
;      9  Electrode  8           RA1   0   0   0   1   inner
;     10  Electrode  9           RA1   1   0   1   0   edge
;     11  Electrode 10           RA1   1   0   0   1   inner
;     12  Electrode 11           RA1   0   1   1   0   edge
;     13  Electrode 12           RA1   0   1   0   1   inner
;     14  Electrode 13           RA1   1   1   1   0   edge
;     15  Electrode 14           RA1   1   1   0   1   inner
;
;     16  Electrode 15           RA3   0   0   1   0   edge
;     17  Electrode 16           RA3   0   0   0   1   inner
;     18  Electrode 17           RA3   1   0   1   0   edge
;     19  Electrode 18           RA3   1   0   0   1   inner
;     20  Electrode 19           RA3   0   1   1   0   edge
;     21  High Threshold         RA3   0   1   0   1   inner
;     22  Q-factor               RA3   1   1   1   0   edge  bottom
;     23  Shift                  RA3   1   1   0   1   inner bottom
;

        LIST p=16C74, n=1000

#include "P16CXX.INC"
#include "goodies.inc"
#include "oi.inc"
#include "shared.inc"

        ; If OPTREX is #define'd, then the LCD module's code is included
        ; NOTE: POT board must be plugged in or OI uP will get caught
        ;       in the polling loop when OPTREX is #defined
#define OPTREX

        org 0x0                 ; reset vector
        goto startup

        org 0x04                ; interrupt vector
        goto interruptPoll

;------------------------------------------------------------
startup
        bcf INTCON,GIE          ; intcon[7]   disable interrupts
        bsf pcon,NOT_POR        ; pcon[1]

        bcf STATUS,RP0          ; bank select bits
        bcf STATUS,RP1

                ; initialize port A
        bsf STATUS,RP0          ; bank 1 for access to TRISA
        movlw 0x3F              ; all bits are input
        movwf trisa
        movlw 0x04              ; bits 0,1,3 are analog w/ Vref=VDD
        movwf adcon1            ;
        bcf STATUS,RP0          ; select bank 0
        clrf PORTA              ; write 0's
                                ; ADCON0 is set only when in use

                ; initialize port B
        bsf STATUS,RP0          ; select bank 1
        movlw 0xC0              ; bits 0-5 are outputs
        movwf trisb
        bsf option,7            ; port B weak pullups disabled
        bcf STATUS,RP0          ; select bank 0
        clrf PORTB

                ; initialize port C
        bsf STATUS,RP0          ; select bank 1
        movlw 0x80              ; bits 0-6 are outputs
        movwf trisc
        bcf STATUS,RP0          ; select bank 0
        clrf PORTC

                ; initialize port D
        bsf STATUS,RP0          ; select bank 1
        clrf trisd              ; Config as outputs
        bcf STATUS,RP0          ; select bank 0
        clrf PORTD

                ; initialize port E
        bsf STATUS,RP0          ; select bank 1
        clrf trise              ; status LED outputs
        bcf STATUS,RP0          ; select bank 0
        clrf PORTE

                ; force the echo LED on
        call forceEchoLED


                ; reset the APPLY button latch
        call resetApplyButton


                ; clear all file registers
        bcf STATUS,RP0          ; select bank 0
        movlw 0x20              ; bank 0 starting at 0x20
        movwf FSR
su3
        clrf INDF
        incf FSR,F
        btfss FSR,7             ; bit 7 started out clear
        goto su3

        bsf STATUS,RP0          ; select bank 1
        movlw 0xA0              ; bank 1 starting at 0xA0
        movwf FSR
su4
        clrf INDF
        incf FSR,F
        btfsc FSR,7             ; bit 7 started out set
        goto su4


                ; initialize the SCI for communication w/ Master uC
        bsf STATUS,RP0          ; select bank 1 for SPBRG,TXSTA
        movlw 0x7
        movwf spbrg             ; 19.x Kbaud at 10 MHz
        bcf txsta,2             ; BRGH low speed
        bcf txsta,4             ; asynchronous mode
        bcf txsta,5             ; transmit disabled
        bcf txsta,6             ; 8-bit data
       ;bcf txsta,7             ; don't care for asynch
        bcf STATUS,RP0          ; select bank 0 for RCSTA
        bsf RCSTA,4             ; enable reception
       ;bcf RCSTA,5             ; don't care for asynch
        bcf RCSTA,6             ; 8-bit data
        bsf RCSTA,7             ; Configure RC6,7 as serial IO pins
        bsf STATUS,RP0          ; select bank 1 for PIE1
        bcf pie1,5               ; disable receive_buffer_full interrupt
        bcf pie1,4               ; disable transmit_buffer_empty interrupt

                        ; ripple the status LEDs
        bcf STATUS,RP0
        movlw 8
        movwf TMP
ledripple
        decf TMP,F
        movfw TMP
        call ledSet
        call visibleDelay
        call visibleDelay
        call visibleDelay
        call visibleDelay
        call visibleDelay
        call visibleDelay
        call visibleDelay
        call ledClear
        movf TMP,F
        btfss STATUS,Z
        goto ledripple

stim1
       ;movlw 0x81
       ;call ledFlash

                        ; reset SCI reception
        bcf STATUS,RP0          ; select bank 0 for PIE1
        bcf RCSTA,4             ; clear ...
        bsf RCSTA,4             ;       ... and set CREN bit (clears OERR bit)
        movfw RCREG             ; clears FERR bit

        call sciReadByte        ; expect master to send a query
       ;call ledFlash           ; flash byte from master
        sublw LED_BOOT_M2O      ; received byte should be LED_BOOT_M2O
        btfsc STATUS,Z
        goto m2ook
        movlw LED_BADM2O        ; if not, flash error
        call ledFlash
m2ook
        movlw LED_BOOT_O2M
        call sciSendByte        ; reply to master "I'm awake"


                        ; set up TMR1 as an interrupt source.
        bcf STATUS,RP0
        clrf TMR1L              ; reset TMR1 value
        clrf TMR1H
        bsf T1CON,T1CKPS1       ; set TMR1 prescaler to 8:1
        bsf T1CON,T1CKPS0
        bcf T1CON,T1OSCEN       ; disable the TMR1 oscillator
        bcf T1CON,T1INSYNC      ; known value, but ignored
        bcf T1CON,TMR1CS        ; set clk source to Fosc/4
        bsf T1CON,TMR1ON        ; enable TMR1
        bcf PIR1,TMR1IF         ; clear TMR1 interrupt flag
        bsf STATUS,RP0          ; for access to PIE1
        bsf pie1,TMR1IE         ; enable the TMR1 interrupt
        bcf STATUS,RP0          ; restore
        bsf INTCON,PEIE         ; enable peripheral interrupts

        bsf INTCON,GIE          ; enable interrupts

#ifdef OPTREX
        bsf PCLATH,3    ; paging
        call lcdInit
        bcf PCLATH,3    ; paging
#endif

        movlw 1                 ; Since interrupts stopped working, we'll
        movwf UPDATEPOTLEDS     ; force POT LEDs to power up correctly

        goto pollLoop
                        ; end of startup
;-------------------------------------------------------
resetApplyButton
; this routine resets the apply button latch

        movlw LEDBIT_74APPLYN
        call ledSet
rab0
        bcf STATUS,RP0          ; bank 0
        bsf PORTC,4             ; apply a pulse to '74 latch
        nop
        bcf PORTC,4
        call visibleDelay
        btfsc PORTB,7           ; test latch output
        goto rab0
        call ledClear

        return          ; from resetApplyButton
;-------------------------------------------------------
forceEchoLED
; this routine forces the ECHO LED on.
; to actually get into echo mode, you must also call echoButton

        movlw LEDBIT_74ECHO
        call ledSet
fem0
        bcf STATUS,RP0          ; bank 0
        bsf PORTC,5             ; apply a pulse to '74 latch
        nop
        bcf PORTC,5
        btfsc PORTB,6           ; test latch output
        goto fem0
        call ledClear

        clrf OLDB6

        return          ; from forceEchoMode
;-------------------------------------------------------
interruptPoll
;       Interrupt service routine. Skeleton taken from 16C7x data sheet p 136

                ; start of ISR skeleton preamble
ip0
        bcf INTCON,GIE          ; disable interrupts
        btfsc INTCON,GIE
        goto ip0                ; make goddam sure that GIE gets cleared


        movwf W_TEMP            ; must be defined in both banks
        swapf STATUS,0
        bcf STATUS,RP0
        movwf STATUS_TEMP        ; defined in bank 0
                ; end of ISR skeleton preamble


                        ; make the shift LEDs work in 'real-time'
        btfss PIR1,TMR1IF
        goto ipt1done           ; check whether it's a TMR1 interrupt
 if 1
        bsf UPDATEPOTLEDS,0    ; set a flag which will be caught in pollLoop
 else
        movlw 7                 ; just set an led for debug
        call ledSet
        call visibleDelay
        call ledClear
 endif
        bcf PIR1,TMR1IF         ; clear TMR1 interrupt flag
ipt1done


                ; start of ISR skeleton appendix
        swapf STATUS_TEMP,0
        movwf STATUS
        swapf W_TEMP,1
        swapf W_TEMP,0
                ; end of ISR skeleton appendix

        retfie          ; end of interruptPoll
;-------------------------------------------------------
pollLoop
;       This is the main polling loop for the OI uC

                ; check whether there's been an SCI request from master uC
        bcf STATUS,RP0          ; PIR1 in bank 0
        btfsc PIR1,5             ; RCIF bit will be set if a byte has come in
        call sciHandler

       ;goto pollLoop   ; for debug

                ; Check whether ECHO or MOD buttons have been pressed
                ; RB6 will have changed state
        bcf STATUS,RP0          ; PORTB in bank 0
        movfw PORTB
        andlw 0x40
        xorwf OLDB6,0           ; if RB6 matches old value, wreg[6] will be 0
        movwf Wreg
        btfss Wreg,6
        goto echomodidle
                                ; RB6 has changed
        btfss PORTB,6
        goto echopressed
                ; mod button was pressed
modpressed
        bsf OLDB6,6
        call modButton
        goto echomodidle

                ; echo button was pressed
echopressed
        bcf OLDB6,6
        call echoButton

echomodidle

                ; Check whether the Apply button was pressed
        btfss RB7
        goto applyidle
                        ; Apply button was pressed
        call applyButton

;ab1            ; this is done within applyButton
;        bsf RC4         ; clock the apply latch until RB7 clears
;        nop
;        bcf RC4
;        btfsc RB7
;        goto ab1
applyidle

                ; check whether it's time to update the shift & Q LEDs
                ; (an ISR will have set UPDATEPOTLEDS)
        movf UPDATEPOTLEDS,F
        btfss UPDATEPOTLEDS,0
        goto potledsdone

        call readShiftPot
        call readQPot
        clrf UPDATEPOTLEDS
potledsdone

#ifdef OPTREX
                ; check for LCD button activity
        bsf PCLATH,3    ; paging
        call pollLCDButtons
        bcf PCLATH,3    ; paging
#endif


        goto pollLoop    ; end of pollLoop
;-------------------------------------------------------
readShiftPot
;       This routine gets called periodically from pollLoop to update the
;       shift pot LEDs

        bcf STATUS,RP0
        bsf PORTC,0             ; address POT 23
        bsf PORTC,1
        bcf PORTC,2
        bcf PORTC,3
        bsf PORTC,3
        movlw 3
        call readPot
        movwf POTVAL23
        bcf PORTC,3

                ; take the 3 msbits of the shift POT and send them to
                ; the shift pot LED decoder at RB0-2
                ; This algorithm should match that used in master.asm
                ; when converting POTVAL23 to MC_SHIFT
        andlw 0xE0              ; take 3 msb's
        movwf Wreg
        rrf Wreg,F              ; rotate to msb's to lsb spots
        rrf Wreg,F
        rrf Wreg,F
        rrf Wreg,F
        rrf Wreg,F
        movfw Wreg              ; Wreg is 0-7
        btfsc Wreg,2            ; if wreg is 4-7 ...
        decf Wreg,F             ;                ... make it 4-6
                                ; Wreg is 0-6
       ;decf Wreg,F
       ;decf Wreg,F
       ;decf Wreg,F             ; Wreg is -3 to +3

        bcf PORTB,0
        btfsc Wreg,0
        bsf PORTB,0
        bcf PORTB,1
        btfsc Wreg,1
        bsf PORTB,1
        bcf PORTB,2
        btfsc Wreg,2
        bsf PORTB,2
        return          ; from readShiftPot
;-------------------------------------------------------
readQPot
;       This routine gets called periodically from pollLoop to update the
;       Q-factor pot LEDs

        bcf STATUS,RP0
        bsf PORTC,0             ; address POT 22
        bsf PORTC,1
        bcf PORTC,2
        bcf PORTC,3
        bsf PORTC,2
        movlw 3
        call readPot
        movwf POTVAL22
        bcf PORTC,2

                ; take the 3 msbits of the Q-factor POT and send them to
                ; the Q-factor pot LED decoder at RB3-5
                ; This algorithm should match that used in master.asm
                ; when converting POTVAL22 to MC_Q
        andlw 0xE0              ; take 3 msb's
        movwf Wreg
        rrf Wreg,F              ; rotate to msb's to lsb spots
        rrf Wreg,F
        rrf Wreg,F
        rrf Wreg,F
        rrf Wreg,F
        movfw Wreg              ; Wreg is 0-7
        btfsc Wreg,2            ; if wreg is 4-7 ...
        decf Wreg,F             ;                ... make it 4-6
        movfw Wreg
                                ; Wreg is 0-6
                                ; Since POT22 is hooked up 'backwards'
        sublw 6                 ; we want 6-this_value.
        movwf Wreg

        bcf PORTB,3
        btfsc Wreg,0
        bsf PORTB,3
        bcf PORTB,4
        btfsc Wreg,1
        bsf PORTB,4
        bcf PORTB,5
        btfsc Wreg,2
        bsf PORTB,5

        return          ; from readQPot
;-------------------------------------------------------
readPot
;       This routine reads a POT value by doing an A/D conversion.
;       On entry, WREG[0-2] is expected to be set to the right bits
;       to be copied to ADCON0[CHSx]:
;               000=A0  001=A1 011=A3
;       On return, wreg contains the analog reading.

        bcf STATUS,RP0          ; bank 0

        bcf ADCON0,CHS0         ; set CHS0
        movwf Wreg
        btfsc Wreg,0
        bsf ADCON0,CHS0

        bcf ADCON0,CHS1         ; set CHS1
        movwf Wreg
        btfsc Wreg,1
        bsf ADCON0,CHS1

        bcf ADCON0,CHS2         ; set CHS2
        movwf Wreg
        btfsc Wreg,2
        bsf ADCON0,CHS2

        bcf ADCON0,ADCS0        ; select Fosc/32 conversion clock
        bsf ADCON0,ADCS1

        bsf ADCON0,ADON         ; turn A/D module on

                        ; wait the required sampling time 12 uS recommended
        movlw 0xFF      ; FF-->75uS at 10 MHz
rp1
        movwf Wreg
        decfsz Wreg,W
        goto rp1

        bsf ADCON0,GO_DONE      ; start conversion

                        ; poll ADCON0 GODONE bit
rp2
        btfsc ADCON0,GO_DONE
        goto rp2

        movfw ADRES             ; put result into WREG

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

        bcf STATUS,RP0          ; access to TXREG
        movwf TXREG             ; load data to be sent
        bsf STATUS,RP0          ; access to TXSTA
        bsf txsta,5             ; enable transmission

        bcf STATUS,RP0          ; access to PORTx
        movlw LEDBIT_SCISEND
        call ledSet

                        ; wait till transmission is done
                        ; (TXIF bit (PIR1[4]) will be set)
        bcf STATUS,RP0  ; access to PIR1
scis1
        btfss PIR1,4     ; 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
        bcf STATUS,RP0          ; PIR1 in bank 0

        movlw LEDBIT_SCIREAD
        call ledSet
srb1
        btfss PIR1,5            ; RCIF bit will be set if a byte has come in
        goto srb1

        call ledClear           ; reset status LEDs

        bcf STATUS,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           ; use status LEDs to flag a framing error
        clrf SRBRECDBYTE
        return
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
        return
srbnooverrun
        call ledClear
                        ; 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 gets called from pollloop when an SCI reception
;       is detected.
;       The protocol used for talking with the master 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 sciReadByte        ; read SCI port. Incoming byte returned 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 ...
        subwf SRBRECDBYTE,0     ; w=SRBRECDBYTE-SCICMD_DUMMY
        movwf Wreg
        movf Wreg,1
        btfss STATUS,Z
        goto scih1
        call sciresp_dummy      ; ... call the DUMMY handler
        goto scih_complete
scih1

        movlw SCICMD_POTVALS    ; if it's a POTVALS   request ...
        subwf SRBRECDBYTE,0     ; w=SRBRECDBYTE-SCICMD_POTVALS
        movwf Wreg
        movf Wreg,1
        btfss STATUS,Z
        goto scih2
        call sciresp_potvalreq  ; ... call the POTVALREQ handler
        goto scih_complete
scih2
        movlw SCICMD_FRAMEDATA  ; if it's a FRAMEDATA message ...
        subwf SRBRECDBYTE,0     ; w=SRBRECDBYTE-SCICMD_FRAMEDATA
        movwf Wreg
        movf Wreg,1
        btfss STATUS,Z
        goto scih3
        call sciresp_framedata  ; ... call the FRAMEDATA handler
        goto scih_complete
scih3

        call sciresp_error       ; None of the above. Reply with ERROR

scih_complete

        bcf STATUS,RP0
        return          ; from sciHandler
;-------------------------------------------------------
sciresp_error
;       This routine is called when an un-recognized SCICMD_* is received
;       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 is a response to a 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_potvalreq
;       This routine is a response to a SCICMD_POTVALREQ request

        call readPotVals

        call shipPotVals        ; '24' + 24 POTVALs + listen for ACK

        return          ; from sciresp_potvalreq
;--------------------------------------------------------
sciresp_framedata
;       This routine is a response to a SCICMD_FRAMEDATA packet
;       It receives the frame data and displays it on the LCD

        movlw 0
        call sciSendByte        ; tell master to go ahead

        call sciReadByte
        movwf COUNT
        sublw .22
        btfss STATUS,Z          ; check that COUNT==.22
        goto gfdnot22
        movlw UNION             ; destination of the 22 values to come (bank 1)
        movwf FSR
gfdloop
        call sciReadByte
        bsf STATUS,RP0
        movwf INDF              ; store data in bank1 buffer UNION
        bcf STATUS,RP0
        incf FSR,F
        decfsz COUNT,F
        goto gfdloop

        movlw 0
        call sciSendByte        ; send acknowlegement

        bsf PCLATH,3    ; paging
        call lcdShowFrame       ; show frame on LCD
        bcf PCLATH,3    ; paging

        goto gfdend

gfdnot22
        movlw LED_GFDNOT22
        call ledFlash
gfdend

        return          ; from sciresp_framedata
;--------------------------------------------------------
getMapValues
;       This routine gets the MAP T's, C's, and R's from the master
;       in response to an LCD button request.

        movlw SCICMD_SHIPMAP
        call sciSendByte        ; request that the master send MAP values

        call sciReadByte
        movwf COUNT
        sublw .60
        btfss STATUS,Z          ; check that COUNT==.60
        goto gmvnot60
        movlw UNION             ; destination of the 60 values to come (bank 1)
        movwf FSR
gmvloop
        call sciReadByte
        bsf STATUS,RP0
        movwf INDF              ; store data in bank1 buffer UNION
        bcf STATUS,RP0
        incf FSR,F
        decfsz COUNT,F
        goto gmvloop

        movlw 0
        call sciSendByte        ; send acknowlegement
        goto gmvend

gmvnot60
        movlw LED_GMVNOT60
        call ledFlash
gmvend
        return          ; from getMapValues
;--------------------------------------------------------
getMCValues
;       This routine gets the MC_ T's, C's, and R's from the master
;       in response to an LCD button request.

        movlw SCICMD_SHIPMC
        call sciSendByte        ; request that the master send MC values

        call sciReadByte
        movwf COUNT
        sublw .60
        btfss STATUS,Z          ; check that COUNT==.60
        goto gmcvnot60
        movlw UNION             ; destination of the 60 values to come (bank 1)
        movwf FSR
gmcvloop
        call sciReadByte
        bsf STATUS,RP0
        movwf INDF              ; store data in bank1 buffer UNION
        bcf STATUS,RP0
        incf FSR,F
        decfsz COUNT,F
        goto gmcvloop

        movlw 0
        call sciSendByte        ; send acknowlegement
        goto gmcvend

gmcvnot60
        movlw LED_GMVNOT60
        call ledFlash
gmcvend
        return          ; from getMCValues
;--------------------------------------------------------
requestFrame
;       This routine requests a recent FRAME from the master
;       in response to an LCD button request.

        movlw SCICMD_SHIPFRAME
        call sciSendByte        ; request that the master send FRAME values
                                ; actual frame data will come later in an
                                ; SCICMD_FRAMEDATA packet

        call sciReadByte        ; master replies with an ACK
        andlw 0xFF              ; check that it's 0
        btfsc STATUS,Z
        goto gfrend
        movlw LED_GFRBADACK
        call ledFlash
gfrend

        return          ; from requestFrame
;--------------------------------------------------------
shipPotVals
;       This routine sends '24' + 24 POTVALs and listens for the ACK
;       It's called from a couple of places, so making it a routine avoids
;       code duplication.

        movlw .24               ; # of bytes to follow
        movwf SPVTMP
        call sciSendByte

        movlw POTVAL0           ; starting with POTVAL0 ...
        movwf FSR
pvr1
        movfw INDF              ; get the POTVAL into wreg ...
        call sciSendByte        ; and send it
        incf FSR,F
        decfsz SPVTMP,F
        goto pvr1

        call sciReadByte        ; wait for ACK
        movwf Wreg
        movf Wreg,F             ; check that it's a 0
        btfsc STATUS,Z
        goto pvr2

        movlw LED_SPVBADACK2    ; bad ACK byte
        call ledFlash
pvr2
       ;call ledClear           ; never actually set it

        return          ; from shipPotVals
;--------------------------------------------------------
modButton
;       This routine is called from pollloop when the mod button is pressed

        movlw SCICMD_SETMOD     ; send a SETMOD command to the master
        call sciSendByte

        call sciReadByte        ; get response
        movwf Wreg
        movf Wreg,1             ; wreg should be 0
        btfsc STATUS,Z
        goto modbuttondone
        movlw LED_BADMODRESP    ; status LED error signal
        call ledFlash

modbuttondone
        return          ; from modButton
;--------------------------------------------------------
echoButton
;       This routine is called from pollloop when the echo button is pressed

        movlw SCICMD_SETECHO    ; send a SETECHO command to the master
        call sciSendByte

        call sciReadByte        ; get response
        movwf Wreg
        movf Wreg,1             ; wreg should be 0
        btfsc STATUS,Z
        goto echobuttondone
        movlw LED_BADECHORESP   ; status LED error signal
        call ledFlash

echobuttondone
        return          ; from echoButton
;--------------------------------------------------------
applyButton
;       This routine is called from pollloop when the apply button is pressed

        call readPotVals        ; Read all the POT values.
                                ; Send POT values to master
        call sendPotVals        ; in a separate SCICMD_POTVALS message.

       ;btfss PORTB,6           ; check whether we're in MOD mode
       ;goto applysent          ; if not skip the APPLY command

        movlw SCICMD_APPLY      ; send the APPLY command
        call sciSendByte
        call sciReadByte        ; ignore the ACK

applysent
                        ; these are done in readPotVals
       ;call readShiftPot       ; update the shift & Q-factor LEDs
       ;call readQPot

                ; reset the APPLY button latch
        call resetApplyButton

        return          ; from applyButton
;-------------------------------------------------------
readPotVals
;       This routine reads the POTs into POTVAL0-POTVAL23
;       A description of how to decode each pot is at the top of this file

        bcf PORTC,2
        bcf PORTC,3

        bcf PORTC,0
        bcf PORTC,1
        bsf PORTC,2
        movlw 0
        call readPot
        movwf POTVAL0
        movlw 1
        call readPot
        movwf POTVAL8
        movlw 3
        call readPot
        movwf POTVAL16
        bcf PORTC,2

        bcf PORTC,0
        bcf PORTC,1
        bsf PORTC,3
        movlw 0
        call readPot
        movwf POTVAL1
        movlw 1
        call readPot
        movwf POTVAL9
        movlw 3
        call readPot
        movwf POTVAL17
        bcf PORTC,3

        bsf PORTC,0
        bcf PORTC,1
        bsf PORTC,2
        movlw 0
        call readPot
        movwf POTVAL2
        movlw 1
        call readPot
        movwf POTVAL10
        movlw 3
        call readPot
        movwf POTVAL18
        bcf PORTC,2

        bsf PORTC,0
        bcf PORTC,1
        bsf PORTC,3
        movlw 0
        call readPot
        movwf POTVAL3
        movlw 1
        call readPot
        movwf POTVAL11
        movlw 3
        call readPot
        movwf POTVAL19
        bcf PORTC,3

        bcf PORTC,0
        bsf PORTC,1
        bsf PORTC,2
        movlw 0
        call readPot
        movwf POTVAL4
        movlw 1
        call readPot
        movwf POTVAL12
        movlw 3
        call readPot
        movwf POTVAL20
        bcf PORTC,2

        bcf PORTC,0
        bsf PORTC,1
        bsf PORTC,3
        movlw 0
        call readPot
        movwf POTVAL5
        movlw 1
        call readPot
        movwf POTVAL13
        movlw 3
        call readPot
        movwf POTVAL21
        bcf PORTC,3

        bsf PORTC,0
        bsf PORTC,1
        bsf PORTC,2
        movlw 0
        call readPot
        movwf POTVAL6
        movlw 1
        call readPot
        movwf POTVAL14
       ;movlw 3                 ; no point in reading POT22 twice
       ;call readPot
       ;movwf POTVAL22
        bcf PORTC,2

        bsf PORTC,0
        bsf PORTC,1
        bsf PORTC,3
        movlw 0
        call readPot
        movwf POTVAL7
        movlw 1
        call readPot
        movwf POTVAL15
       ;movlw 3                 ; no point in reading POT23 twice
       ;call readPot
       ;movwf POTVAL23
        bcf PORTC,3

                        ; update OI LED values while you're at it.
        call readQPot           ; POTVAL22
        call readShiftPot       ; POTVAL23

        return  ; from readPotVals
;-------------------------------------------------------
sendPotVals
;       This routine sends a SCICMD_POTVALS message

        movlw SCICMD_POTVALS
        call sciSendByte

        movlw LEDBIT_SENDPOTVALS
        call ledSet
        call sciReadByte        ; get ACK byte
        movwf Wreg
        movf Wreg,1             ; check that it's a 0
        btfsc STATUS,Z
        goto spv0

        movlw LED_SPVBADACK1    ; bad ACK byte
        call ledFlash
        return
spv0

        call shipPotVals        ; '24' + 24 POTVALs + listen for ACK

        return  ; from sendPotVals
;-------------------------------------------------------
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        ; restore so next ledFlash will be normal
        return          ; from ledFastFlash
;-------------------------------------------------------
ledFlash
;       This routine flashes a code to the status LEDs.
;       One bit at a time to create the illusion of concurrent display.
;       The code to be displayed is in wreg on entry

        bcf STATUS,RP0          ; 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 REGE 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 RD0                 ; enable status LEDs
        nop
        nop
        nop
        bcf RD0                 ; disable status LEDs
ledf0
        rlf LEDSHIFT,1          ; shift left
        movf LEDREGE,F
        btfss STATUS,Z          ; if LEDREGE is 0, we're done
        goto ledf1


                                ; repeat enough times to be visible to naked eye
        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 RD0                 ; disable status LEDs

        bcf STATUS,RP0          ; bank 0 on return  (redundant)
        retlw 0         ; from ledFlash
;-------------------------------------------------------
ledSet
;       This routine turns on a status LED bit. WREG tells which one
        bcf STATUS,RP0          ; bank 0 for access to PORTs
        movwf PORTE
        bsf RD0                 ; enable the LEDs
        return          ; from ledSet
;-------------------------------------------------------
ledClear
;       This disables the status LEDs
        bcf STATUS,RP0          ; bank 0 for access to PORTs
        bcf RD0                 ; disable the LEDs
        return          ; from ledClear
;-------------------------------------------------------
visibleDelay
;       This routine delays long enough to be visible
        movwf VDTMP             ; save

        movlw 0xFF
        movwf Wreg
vd1
        nop
        nop
        nop             ; add NOPs if you increase the clock speed
        nop
        nop
        decfsz Wreg,F
        goto vd1

        movfw VDTMP             ; restore
        return          ; from visibleDelay
;-------------------------------------------------------
;-------------------------------------------------------
;-------------------------------------------------------
#ifdef OPTREX
 #include "oilcd.asm"
#endif
;------------------------------------------------------------
        end
