Datalogger


;--------------------------------------------------------------------------------------------------------------------------------------
;
;       MOTORCYCLE DATA LOGGER - ASSEMBLY SOURCE CODE FOR THE MICROCHIP PIC 16F877
;
;
;       Author  : Andries C. Tip
;                 andries.tip@planet.nl
;       
;       Start of programming    : May 2002
;       Last Revision Date      : December 29, 2006
;
;       Program code written with MPLAB IDE v7.00 from Microchip
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
;       Define firmware version, this number can be read by computer 
;       
        #define fwv0 a'1'       ; version number
        #define fwv1 a'7'       ; build number, most significant
        #define fwv2 a'9'       ; build number, least significant
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
;       History:
;
;       logger179.asm   Bugfix: in the interrupt routine the movwf status_temp came after clrf status !!
;       logger178.asm   Bugfix: bank23 and bank32 macro's were faulty (rp1 instead of rp0) but were not used in the program
;       logger177.asm   First Public Release in datalogger
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
;       Font: Bitstream Vera Sans Mono (size 8 for screen display, size 6 for printing)
;
;       Tabsize: 8 characters
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
;       Functional description of this firmware:
;
;       This firmware is used in a datalogger that will record relevant data from sensors of a motorcycle and
;       consists of a Microchip PIC 16F877, IIC EEPROM chips, IIC clock chip etc. After recording, the data can be
;       read by using a computer with RS232 communication. The computer is also used for configuring the datalogger
;       with settings such as logging rate, wheel diameter, input channels and so forth.
;
;       The logging rate (measurements/second) can be given seperately for each channel.
;
;       The rotary switch is used to start and stop the data logging. Multiple records can be taken before data readout
;       by the computer is needed. Each record will have its own time and date recorded. The mark button can be
;       pressed at any time during the logging and can be used to indicate periods in the computer graphs.
;
;       Note: For reasons of clarity the external eeprom pages (64 bytes each) are called blocks instead of pages to avoid confusion 
;       with the memory pages which are used within the Microchip PIC chip.
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
;       Pinout:
;
;       Port A0 = Analog input 0        Lambda sensor [0..1 V]
;       Port A1 = Analog input 1        Voltage [0..5 V]
;       Port A2 = Analog input 2        K-Type Thermocouple [0..1024? Celcius ?]
;       Port A3 = Analog input 3        Air Temperature (NTC)
;       Port A4 = Digital input         Clock input 32768 Hz from Clock/Timer chip, used as interrupt
;       Port A5 = Analog input 4        Water Temperature (NTC)
;
;       Port B0 = Digital input         Run
;       Port B1 = Digital input         _Mark_
;       Port B2 = Digital input         Brake
;       Port B3 = Digital input         Laptime
;       Port B4 = Digital input         _TC disconnected_
;       Port B5 = Digital input         BoardsupplyOn
;       Port B6 = Digital output        Status DuoLed Red       
;       Port B7 = Digital output        Status DuoLed Green
;
;       Port C0 = Digital output        Computer CTS
;       Port C1 = Digital input         Speed [0..255 km/hr]
;       Port C2 = Digital input         RPM [0..16383 RPM]
;       Port C3 = Digital in/output     IIC SDA, clock chip, serial eeproms
;       Port C4 = Digital output        IIC SCL, clock chip, serial eeproms
;       Port C5 = Digital input         Computer RTS
;       Port C6 = Digital output        Computer RX (configure as input so the usart can use this pin !)
;       Port C7 = Digital input         Computer TX
;
;       Port D0 = Digital output        Led0, leds are rpm indicator/shift light
;       Port D1 = Digital output        Led1
;       Port D2 = Digital output        Led2
;       Port D3 = Digital output        Led3
;       Port D4 = Digital output        Led4
;       Port D5 = Digital output        Led5
;       Port D6 = Digital output        Led6
;       Port D7 = Digital output        Led7
;
;       Port E0 = Analog input 5        Throttle [0..100 %]
;       Port E1 = Analog input 6        Longitudinal acceleration from G-sensor chip
;       Port E2 = Analog input 7        Lateral acceleration from G-sensor chip
;
;--------------------------------------------------------------------------------------------------------------------------------------
; Format of data file as composed by computer from datalogger settings and record data
;--------------------------------------------------------------------------------------------------------------------------------------
;Datalogger data file
;
;[Information]
;Serial number=2743
;Start of logging=12/12/04 15:33:57
;Original filename=D:\Datalogger\Data\Track_0018.dat
;Record size=2093104                                                    << record size in bits
;
;[Errors]
;Log event overflow=1
;Thermocouple disconnected=0
;Memory full=1
;
;[Settings]
;Wheel circumference=1967
;Wheel radius=313.1
;RPM maximum=5000
;
;[Channels]
;RPM=32
;Speed=32
;Throttle=32
;Thermocouple=32
;Lambda=32
;Voltage=32
;Airtemp=32
;Watertemp=32
;LongAccel=32
;LatAccel=32
;Mark=32
;Brake=32
;
;[Data]
;0  PC 0  PC 0  L.....                                << raw data
;
; End of file.                                                          << just to inform user
;
;--------------------------------------------------------------------------------------------------------------------------------------
; PIC 14-bit core instruction set
;--------------------------------------------------------------------------------------------------------------------------------------
;
; Mnemonic                      Description                     Function                        Status affected         Instr.Cycles
;
; ADDLW         k               Add literal to W                k + W > W                       C, DC, Z                1
; ADDWF         f,d             Add W and f                     W + f >d                        C, DC, Z                1
; SUBLW         k               Subtract W from literal         k - W > W                       C, DC, Z                1
; SUBWF         f,d             Subtract W from f               f - W > d                       C, DC, Z                1
; BCF           f, b            Bit clear f                     0 > f ( b )                                             1
; BSF           f, b            Bit set f                       1 > f ( b )                                             1
; BTFSC         f, b            Bit test, skip if clear         skip if f ( b ) = 0                                     1(2)*
; BTFSS         f, b            Bit test, skip if set           skip if f ( b ) = 1                                     1(2)*
; CLRF          f               Clear f                         0 >f                            Z                       1
; CLRW                          Clear W                         0 >W                            Z                       1
; INCF          f,d             Increment f                     f + 1 > d                       Z                       1
; INCFSZ        f,d             Increment f, skip if zero       f + 1 > d, skip if 0                                    1(2)*
; DECF          f,d             Decrement f                     f - 1 > d                       Z                       1
; DECFSZ        f,d             Decrement f, skip if zero       f - 1 > d, skip if 0                                    1(2)*
; GOTO          k               Goto address (k is nine bits)   k > PC (9 bits)                                         1
; CALL          k               Call subroutine                 PC + 1 > TOS, k >PC                                       2
; RETURN                        Return from subroutine          TOS > PC                                                  2
; RETLW         k               Return with literal in W        k > W, TOS > PC                                           2
; RETFIE                        Return from interrupt           TOS > PC, 1 > GIE                                         2
; MOVLW         k               Move literal to W               k > W                                                   1
; MOVF          f,d             Move f                          f > d                           Z                       1
; MOVWF         f               Move W to f                     W > f                                                   1
; SWAPF         f,d             Swap halves f                   f ( 0:3 ) > f ( 4:7)  > d                               1
; RLF           f,d             Rotate left through carry       C < 76543210 <                  C                       1
; RRF           f,d             Rotate right through carry      C > 76543210 >                  C                       1
; COMF          f,d             Complement f                    .NOT. F > d                     Z                       1
; ANDLW         k               AND literal and W               k .AND. W > W                   Z                       1
; ANDWF         f,d             AND W and f                     W .AND. F >d                    Z                       1
; IORLW         k               Inclusive OR literal and W      k .OR. W > W                    Z                       1
; IORWF         f,d             Inclusive OR W and f            W .OR. F >d                     Z                       1
; XORLW         k               Exclusive OR literal and W      k .XOR. W > W                   Z                       1
; XORWF         f,d             Exclusive OR W and f            W .XOR.  F >d                   Z                       1
; NOP                           No operation                                                                            1
; OPTION                        Load OPTION register            W > OPTION Register                                     1
; CLRWDT        T               Clear watchdog timer            0 > WDT (and prescaler)         _TO , _PD               1
; SLEEP                         Go into standby Mode            0 > WDT, stop oscillator        _TO , _PD               1
; TRIS          f               Tristate port f (only A,B,C!)   W > I/O control register f                              1
;
;
; * = If the program counter (PC) is modified, or a conditional test is true, the instruction requires two cycles. The second
;     cycle is executed as a NOP
;
;
; Field description:
;
; f     register file address (0x00 to 0x7F)
; w     working register (accumulator)
; b     bit address within an 8-bit file register
; k     literal field, constant data or label
; d     destination select, d = 0 means store result in w, d = 1 means store result in file register f (default is d = 1)
; PC    program counter
; TO    time-out bit
; PD    power-down bit
;
;
; Subtraction:  Carry = 1       result is positive or zero
;               Carry = 0       result is negative (borrow)
; Addition:     Carry = 1       result is > 255 (overflow)
;               Carry = 0       result is <=255
;
;--------------------------------------------------------------------------------------------------------------------------------------
; Configuration
;--------------------------------------------------------------------------------------------------------------------------------------

        list    p=16f877                ; directive to define processor

        radix   dec                     ; all numbers are decimal unless stated otherwise 

        errorlevel      -219            ; suppress 'Invalid RAM location specified' warnings (editor warns unneededly about these
                                        ; addresses because of unused registers at the same locations in bank1
        errorlevel      -302            ; suppress 'not in register bank' warnings
        errorlevel      -305            ; suppress 'default destination' warnings
        errorlevel      -306            ; suppress 'crossing page boundary' warnings


;--------------------------------------------------------------------------------------------------------------------------------------
; Version
;--------------------------------------------------------------------------------------------------------------------------------------

        __IDLOCS h'0000'                ; store the firmware version number in the pic identification memory locations

;--------------------------------------------------------------------------------------------------------------------------------------
; Name registers
;--------------------------------------------------------------------------------------------------------------------------------------

w               EQU     H'0000'         ; used in instructions like decsfz
f               EQU     H'0001'         ; to indicate destination

;----- Register Files--------------------------------------------------

; BANK 0 :

indf            EQU     H'0000'         ; indirect file register 
tmr0            EQU     H'0001'         ; timer 0 module register
pcl             EQU     H'0002'         ; program counter (low byte)
status          EQU     H'0003'         ; status register
fsr             EQU     H'0004'         ; indirect data memory address pointer
porta           EQU     H'0005'         ; port A
portb           EQU     H'0006'         ; port B
portc           EQU     H'0007'         ; port C
portd           EQU     H'0008'         ; port D
porte           EQU     H'0009'         ; port E
pclath          EQU     H'000A'         ; write buffer for program counter (upper 5 bits)
intcon          EQU     H'000B'         ; interrupt control register
pir1            EQU     H'000C'         ; peripheral interrupt flags
pir2            EQU     H'000D'         ; CCP 2/SSP bus collision/eeprom write operation interrupt flags
tmr1l           EQU     H'000E'         ; timer 1 register low byte
tmr1h           EQU     H'000F'         ; timer 1 register high byte
t1con           EQU     H'0010'         ; timer 1 control register
tmr2            EQU     H'0011'         ; timer 2 
t2con           EQU     H'0012'         ; timer 2 control register
sspbuf          EQU     H'0013'         ; SSP receive/xmit register
sspcon          EQU     H'0014'         ; SSP control register 1
ccpr1l          EQU     H'0015'         ; CCP 1 low byte
ccpr1h          EQU     H'0016'         ; CCP 1 high byte
ccp1con         EQU     H'0017'         ; CCP 1 control register
rcsta           EQU     H'0018'         ; UART receive status and control register
txreg           EQU     H'0019'         ; UART xmit data register
rcreg           EQU     H'001A'         ; UART receive register
ccpr2l          EQU     H'001B'         ; CCP 2 low byte
ccpr2h          EQU     H'001C'         ; CCP 2 high byte
ccp2con         EQU     H'001D'         ; CCP 2 control register
adresh          EQU     H'001E'         ; A/D result register high byte
adcon0          EQU     H'001F'         ; A/D operation control register

; BANK 1 :

optionreg       EQU     H'0081'         ; option register
trisa           EQU     H'0085'         ; Port A data direction control register
trisb           EQU     H'0086'         ; Port B data direction control register
trisc           EQU     H'0087'         ; Port C data direction control register
trisd           EQU     H'0088'         ; Port D data direction control register
trise           EQU     H'0089'         ; Port E data direction control register
pie1            EQU     H'008C'         ; peripheral interrupt enable
pie2            EQU     H'008D'         ; CCP 2/SSP bus collision/eeprom write operation interrupt enable
pcon            EQU     H'008E'         ; power control register
sspcon2         EQU     H'0091'         ; SSP control register 2
pr2             EQU     H'0092'         ; timer 2 period register
sspadd          EQU     H'0093'         ; SSP (I2C mode) address register
sspstat         EQU     H'0094'         ; SSP status register
txsta           EQU     H'0098'         ; UART xmit status and control register
spbrg           EQU     H'0099'         ; UART baud rate generator speed control value
adresl          EQU     H'009E'         ; A/D result register low byte
adcon1          EQU     H'009F'         ; A/D pin control register

; BANK 2 :

eedata          EQU     H'010C'         ; eeprom data register low byte
eeadr           EQU     H'010D'         ; eeprom address register low byte
eedath          EQU     H'010E'         ; eeprom data register high byte
eeadrh          EQU     H'010F'         ; eeprom address register high byte

; BANK 3 :

eecon1          EQU     H'018C'         ; eeprom control register 1
eecon2          EQU     H'018D'         ; eeprom control register 2

;----- STATUS Bits ----------------------------------------------------

irp             EQU     7
rp1             EQU     6
rp0             EQU     5
not_to          EQU     4
not_pd          EQU     3
z               EQU     2
dc              EQU     1
c               EQU     0

;----- INTCON Bits ----------------------------------------------------

gie             EQU     7
peie            EQU     6
t0ie            EQU     5
inte            EQU     4
rbie            EQU     3
t0if            EQU     2
intf            EQU     1
rbif            EQU     0

;----- PIR1 Bits ------------------------------------------------------

pspif           EQU     7
adif            EQU     6
rcif            EQU     5
txif            EQU     4
sspif           EQU     3
ccp1if          EQU     2
tmr2if          EQU     1
tmr1if          EQU     0

;----- PIR2 Bits ------------------------------------------------------

eeif            EQU     4
bclif           EQU     3
ccp2if          EQU     0

;----- T1CON Bits -----------------------------------------------------

t1ckps1         EQU     5
t1ckps0         EQU     4
t1oscen         EQU     3
t1sync          EQU     2
tmr1cs          EQU     1
tmr1on          EQU     0

;----- T2CON Bits -----------------------------------------------------

toutps3         EQU     6
toutps2         EQU     5
toutps1         EQU     4
toutps0         EQU     3
tmr2on          EQU     2
t2ckps1         EQU     1
t2ckps0         EQU     0

;----- SSPCON Bits -----------------------------------------------------

wcol            EQU     7
sspov           EQU     6
sspen           EQU     5
ckp             EQU     4
sspm3           EQU     3
sspm2           EQU     2
sspm1           EQU     1
sspm0           EQU     0

;----- CCP1CON Bits ----------------------------------------------------

ccp1x           EQU     5
ccp1y           EQU     4
ccp1m3          EQU     3
ccp1m2          EQU     2
ccp1m1          EQU     1
ccp1m0          EQU     0

;----- RCSTA Bits ----------------------------------------------------

spen            EQU     7
rx9             EQU     6
sren            EQU     5
cren            EQU     4
adden           EQU     3
ferr            EQU     2
oerr            EQU     1
rx9d            EQU     0

;----- CCP2CON Bits ----------------------------------------------------

ccp2x           EQU     5
ccp2y           EQU     4
ccp2m3          EQU     3
ccp2m2          EQU     2
ccp2m1          EQU     1
ccp2m0          EQU     0

;----- ADCON0 Bits ----------------------------------------------------

adcs1           EQU     7
adcs0           EQU     6
chs2            EQU     5
chs1            EQU     4
chs0            EQU     3
go_notdone      EQU     2
adon            EQU     0

;----- OPTION Register Bits ------------------------------------------

not_rbpu        EQU     7
intedg          EQU     6
t0cs            EQU     5
t0se            EQU     4
psa             EQU     3
ps2             EQU     2
ps1             EQU     1
ps0             EQU     0

;----- TRISE Bits ----------------------------------------------------

ibf             EQU     7
obf             EQU     6
ibov            EQU     5
pspmode         EQU     4
dirpe2          EQU     2
dirpe1          EQU     1
dirpe0          EQU     0

;----- PIE1 Bits ------------------------------------------------------

pspie           EQU     7
adie            EQU     6
rcie            EQU     5
txie            EQU     4
sspie           EQU     3
ccp1ie          EQU     2
tmr2ie          EQU     1
tmr1ie          EQU     0

;----- PIE2 Bits ------------------------------------------------------

eeie            EQU     4
bclie           EQU     3
ccp2ie          EQU     0

;----- PCON Bits ------------------------------------------------------

not_por         EQU     1
not_bor         EQU     0

;----- SSPCON2 Bits ----------------------------------------------------

gcen            EQU     7
ackstat         EQU     6
ackdt           EQU     5
acken           EQU     4
rcen            EQU     3
pen             EQU     2
rsen            EQU     1
sen             EQU     0

;----- SSPSTAT Bits ---------------------------------------------------

smp             EQU     7
cke             EQU     6
d_nota          EQU     5
p               EQU     4
s               EQU     3
r_notw          EQU     2
ua              EQU     1
bf              EQU     0

;----- TXSTA Bits ----------------------------------------------------

csrc            EQU     7
tx9             EQU     6
txen            EQU     5
sync            EQU     4
brgh            EQU     2
trmt            EQU     1
tx9d            EQU     0

;----- ADCON1 Bits ----------------------------------------------------

adfm            EQU     7
pcfg3           EQU     3
pcfg2           EQU     2
pcfg1           EQU     1
pcfg0           EQU     0

;----- EECON1 Bits ----------------------------------------------------

eepgd           EQU     7
wrerr           EQU     3
wren            EQU     2
wr              EQU     1
rd              EQU     0

;--------------------------------------------------------------------------------------------------------------------------------------
; RAM definition
;--------------------------------------------------------------------------------------------------------------------------------------

        __MAXRAM H'1FF'
        __BADRAM H'8F'-H'90', H'95'-H'97', H'9A'-H'9D', H'105', H'107'-H'109', H'185', H'187'-H'189', H'18E'-H'18F'

;--------------------------------------------------------------------------------------------------------------------------------------
; Configuration directive
;--------------------------------------------------------------------------------------------------------------------------------------

; '__CONFIG' directive is used to embed configuration data within .asm file.


_CP_ALL                 EQU     H'0FCF'
_CP_HALF                EQU     H'1FDF'
_CP_UPPER_256           EQU     H'2FEF'
_CP_OFF                 EQU     H'3FFF'
_DEBUG_ON               EQU     H'37FF'
_DEBUG_OFF              EQU     H'3FFF'
_WRT_ENABLE_ON          EQU     H'3FFF'
_WRT_ENABLE_OFF         EQU     H'3DFF'
_CPD_ON                 EQU     H'3EFF'
_CPD_OFF                EQU     H'3FFF'
_LVP_ON                 EQU     H'3FFF'
_LVP_OFF                EQU     H'3F7F'
_BODEN_ON               EQU     H'3FFF'
_BODEN_OFF              EQU     H'3FBF'
_PWRTE_OFF              EQU     H'3FFF'
_PWRTE_ON               EQU     H'3FF7'
_WDT_ON                 EQU     H'3FFF'
_WDT_OFF                EQU     H'3FFB'
_LP_OSC                 EQU     H'3FFC'
_XT_OSC                 EQU     H'3FFD'
_HS_OSC                 EQU     H'3FFE'
_RC_OSC                 EQU     H'3FFF'


        __CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _XT_OSC &_WRT_ENABLE_ON & _LVP_OFF & _DEBUG_OFF & _CPD_OFF 

;--------------------------------------------------------------------------------------------------------------------------------------
; Register bank macro's
;--------------------------------------------------------------------------------------------------------------------------------------

bank0   MACRO                           ; select register bank 0
        bcf     status,rp0
        bcf     status,rp1
        ENDM

bank1   MACRO                           ; select register bank 1
        bsf     status,rp0
        bcf     status,rp1
        ENDM

bank2   MACRO                           ; select register bank 2
        bcf     status,rp0
        bsf     status,rp1
        ENDM

bank3   MACRO                           ; select register bank 3
        bsf     status,rp0
        bsf     status,rp1
        ENDM

bank02  MACRO                           ; change from bank 0 to bank 2
        bsf     status,rp1
        ENDM

bank20  MACRO                           ; change from bank 2 to bank 0
        bcf     status,rp1
        ENDM

bank01  MACRO                           ; change from bank 0 to bank 1
        bsf     status,rp0
        ENDM

bank10  MACRO                           ; change from bank 1 to bank 0
        bcf     status,rp0
        ENDM

bank23  MACRO                           ; change from bank 2 to bank 3
        bsf     status,rp0
        ENDM

bank32  MACRO                           ; change from bank 3 to bank 2
        bcf     status,rp0
        ENDM


;--------------------------------------------------------------------------------------------------------------------------------------
; Program memory page macro's
;--------------------------------------------------------------------------------------------------------------------------------------


page0   MACRO                           ; select program memory page 0 (hex 0000 up to hex 07FF)
        bcf     pclath,3
        bcf     pclath,4
        ENDM

page1   MACRO                           ; select program memory page 0 (hex 0800 up to hex 0FFF)
        bsf     pclath,3
        bcf     pclath,4
        ENDM

page2   MACRO                           ; select program memory page 0 (hex 1000 up to hex 17FF)
        bcf     pclath,3
        bsf     pclath,4
        ENDM

page3   MACRO                           ; select program memory page 0 (hex 17FF up to hex 1FFF)
        bsf     pclath,3
        bsf     pclath,4
        ENDM

;--------------------------------------------------------------------------------------------------------------------------------------
; Constants definitions
;--------------------------------------------------------------------------------------------------------------------------------------


switch          EQU     0       ; port B, rotary switch on datalogger (low=on/high=log)
not_mark        EQU     1       ; port B, marking switch at vehicle handlebar/steer (low=pressed)
brake           EQU     2       ; port B, switch connected to brake handle/pedal (high=pressed)
laptime         EQU     3       ; port B, extra laptime circuit to receive infrared beam from light along race track **** to do
not_tcd         EQU     4       ; port B, thermocouple disconnected (low=disconnected)
boardsuppl      EQU     5       ; port B, external power supply to datalogger (low=battery power only,high=exteral power on)
led_red         EQU     6       ; port B, red status led (see below)
led_green       EQU     7       ; port B, green status led (see below)

; status led:
; red           =  datalogger switched on
; green         = logging
; yellow        = logging, but memory almost full
; flashing red  = one of the following errors:
;                       - logging stopped because memory is full
;                       - no channels enabled for logging
;                       - the table of contents (toc) is full, there is a maximum of 20 records 

rts_in          EQU     5       ; Port C Pin 5 is input for uart flow control
cts_out         EQU     0       ; Port C Pin 0 is output for uart flow control

@tocstart       EQU     d'56'   ; start of table of contents in internal eeprom, runs up to and includes address 255

;--------------------------------------------------------------------------------------------------------------------------------------
; Variable definitions
;--------------------------------------------------------------------------------------------------------------------------------------

; variables used at top of bank 0,1,2,3, available from any bank: (Hex 70-7F)

w_temp          EQU     0x70            ; variable used for context saving during interrupts
status_temp     EQU     0x71            ; variable used for context saving during interrupts
pclath_temp     EQU     0x72            ; variable used for context saving during interrupts
fsr_temp        EQU     0x73            ; variable used for context saving during interrupts
numlow          EQU     0x74            ; least significant or lower byte of 16 bit number (least significant byte of 24 bit number)
nummiddle       EQU     0x75            ; most significant or upper byte of 16 bit number (middle byte of 24 bit number)
numhigh         EQU     0x76            ; extended byte (most significant byte of 24 bit number)
adtemp          EQU     0x77            ; used in a/d conversion for channel selection and as delay counter
adchannel       EQU     0x78            ; channel number used in a/d conversion

templow         EQU     0x7A            ; used in 8, 16 and 24 bit number read from buffer and transmission 
tempmiddle      EQU     0x7B            ; used in 16 and 24 bit number read from buffer and transmission 
temphigh        EQU     0x7C            ; used in 24 bit number transmission 
errors          EQU     0x7D            ; errors flags, bits will be when errors occur during logging (see below)
flags1          EQU     0x7E            ; status flag bits (see below)
flags2          EQU     0x7F            ; status flag bits (see below)

; bits of 'errors' register:
logoflow        EQU     0               ; previous log event was not finished when next event was started, logdata will be corrupted
tcdiscon        EQU     1               ; the thermocouple wire has been disconnected during logging, tc-logdata will be corrupted
err_mf          EQU     2               ; the external eeprom memory got full during logging

; bits of 'flags1' register:

stx             EQU     0               ; stx (start of text) byte received
etx             EQU     1               ; etx (end of text) byte received
withdata        EQU     2               ; command has data bytes
command         EQU     3               ; all bytes of command have been received, now ready for execution of command
changese        EQU     7               ; backdoor to change device serial number

; bits of 'flags2' register:

lognow          EQU     0               ; timer0 interrupt just occured which means data should be logged instantly 
tocfull         EQU     1               ; table of contents in pic internal eeprom is full
memfull         EQU     2               ; external eeproms are all full
rpmbusy         EQU     3               ; do not disturb rpm calculation
spdbusy         EQU     4               ; do not disturb speed calculation
shlight         EQU     5               ; this flag is set when shift light should be on, used in timer interrupt to flash light
norpm           EQU     6               ; indicate we did not get a rpm pulse so calculate a dropping rpm rate
nospeed         EQU     7               ; indicate we did not get a speed pulse so calculate a dropping speed value

; variables used in bank 0 (Hex 20-6F)

rx_byte         EQU     0x20            ; byte received from UART
rx_parity       EQU     0x21            ; byte used in parity checking
rx_checksum     EQU     0x22            ; byte used in receive checksum calculation
rx_pointer      EQU     0x23            ; pointer for command input buffer
rx_maxpos       EQU     0x24            ; maximum location of received command bytes
tx_byte         EQU     0x25            ; byte ready to be sent
tx_checksum     EQU     0x26            ; used in transmission checksum calculation
tx_parity       EQU     0x27            ; used in parity calculation
tx_numberl      EQU     0x28            ; used in 8, 16 and 24 bit number transmission
tx_numberm      EQU     0x29            ; used in 16 and 24 bit number transmission
tx_numberh      EQU     0x2A            ; used in 24 bit number transmission
tx_counter      EQU     0x2B            ; used in 8 and 16 bit number transmission

rx_buffer00     EQU     0x2D            ; start of serial communications command receive buffer:
rx_buffer01     EQU     0x2E            ; register addresses 2D and 2E are used for two command bytes,
rx_buffer02     EQU     0x2F            ; addresses from 2F - 6F are used for a maximum of 64 data bytes
rx_buffer03     EQU     0x30            ;
rx_buffer04     EQU     0x31            ;
rx_buffer05     EQU     0x32            ; 
rx_buffer06     EQU     0x33            ; 
rx_buffer07     EQU     0x34            ; 
rx_buffer08     EQU     0x35            ; 
rx_buffer09     EQU     0x36            ; 
rx_buffer10     EQU     0x37            ; 
rx_buffer11     EQU     0x38            ; 
rx_buffer12     EQU     0x39            ; 
rx_buffer13     EQU     0x3A            ; 
rx_buffer14     EQU     0x3B            ; 
rx_buffer15     EQU     0x3C            ; 
rx_buffer16     EQU     0x3D            ; 
rx_buffer17     EQU     0x3E            ; 
rx_buffer18     EQU     0x3F            ; 
rx_buffer65     EQU     0x6E            ;
rx_buffer66     EQU     0x6F            ; plus one checksum byte (note: same locations as blockbuffer in bank1 !)

; variables used in bank 1 (Hex A0-EF)

iicdata         EQU     0xA0            ; data byte
iicalow         EQU     0xA1            ; address low byte (8 bits)
iicahigh        EQU     0xA2            ; address high byte (7 bits)
iicchip         EQU     0xA3            ; number of external eeprom chip (0..7)

mem_alow        EQU     0xA4            ; address select, low byte
mem_ahigh       EQU     0xA5            ; address select, high byte
mem_chip        EQU     0xA6            ; chip select for (block) write operations to external eeprom

regpointer      EQU     0xA7            ; register pointer in block buffer
bitpointer      EQU     0xA8            ; bit pointer in block buffer
bitcounter      EQU     0xA9            ; temporary counter used in shift operations for block buffer storage
bitmask         EQU     0xAA            ; mask for one bit add operation in block buffer 

databyte0       EQU     0xAB            ; used in copy and shift operations for storing data in the block buffer
databyte1       EQU     0xAC            ; used in copy and shift operations for storing data in the block buffer
databyte2       EQU     0xAD            ; used in copy and shift operations for storing data in the block buffer

toc_pointer     EQU     0xAE            ; pointer for table of contents in internal eepromm

blockbuff00     EQU     0xAF            ; location of first byte in 64 byte block buffer for writes to external eeprom
blockbuff63     EQU     0xEE            ; location of last byte in block buffer (same locations as rx_buffer in bank0 !)

toc_test        EQU     0xEF            ; testing for free location in table of contents in internal eeprom



; variables used in bank 2 (Hex 10-6F)

iee_address     EQU     0x10            ; pointer for write operations to pic internal eeprom
timer1y         EQU     0x11            ; extend range of timer1 with one byte to three bytes
timer1z         EQU     0x12            ; extend range of timer1 with one byte to four bytes
new1cap0        EQU     0x13            ; captures byte 0 of timer 1
new1cap1        EQU     0x14            ; captures byte 1 of timer 1
new1cap2        EQU     0x15            ; captures byte 2 of timer 1
new1cap3        EQU     0x16            ; captures byte 3 of timer 1
old1cap0        EQU     0x17            ; holds previous value of captured byte 0 of timer 1
old1cap1        EQU     0x18            ; holds previous value of captured byte 1 of timer 1
old1cap2        EQU     0x19            ; holds previous value of captured byte 2 of timer 1
old1cap3        EQU     0x1A            ; holds previous value of captured byte 3 of timer 1
new2cap0        EQU     0x1B            ; captures byte 0 of timer 1
new2cap1        EQU     0x1C            ; captures byte 1 of timer 1
new2cap2        EQU     0x1D            ; captures byte 2 of timer 1
new2cap3        EQU     0x1E            ; captures byte 3 of timer 1
old2cap0        EQU     0x1F            ; holds previous value of captured byte 0 of timer 1
old2cap1        EQU     0x20            ; holds previous value of captured byte 1 of timer 1
old2cap2        EQU     0x21            ; holds previous value of captured byte 2 of timer 1
old2cap3        EQU     0x22            ; holds previous value of captured byte 3 of timer 1
divcounter      EQU     0x23            ; used in division routine
nrator0         EQU     0x24            ; used in division routine
nrator1         EQU     0x25            ; used in division routine
nrator2         EQU     0x26            ; used in division routine
nrator3         EQU     0x27            ; used in division routine
denom_r0        EQU     0x28            ; used in division routine of ccp1 (RPM)
denom_r1        EQU     0x29            ; used in division routine
denom_r2        EQU     0x2A            ; used in division routine
denom_r3        EQU     0x2B            ; used in division routine
denom_s0        EQU     0x2C            ; used in division routine of ccp2 (Speed)
denom_s1        EQU     0x2D            ; used in division routine
denom_s2        EQU     0x2E            ; used in division routine
denom_s3        EQU     0x2F            ; used in division routine
remain0         EQU     0x30            ; used in division routine
remain1         EQU     0x31            ; used in division routine
remain2         EQU     0x32            ; used in division routine
remain3         EQU     0x33            ; used in division routine
rpm_low         EQU     0x34            ; engine rpm, result low byte of division
rpm_high        EQU     0x35            ; engine rpm, result high byte of division
speed           EQU     0x36            ; vehicle speed, result byte of division
mult_a0         EQU     0x37            ; used in 16 bit multiplication routine
mult_a1         EQU     0x38            ; used in 16 bit multiplication routine
mult_b0         EQU     0x39            ; used in 16 bit multiplication routine
mult_b1         EQU     0x3A            ; used in 16 bit multiplication routine
speed_const0    EQU     0x3B            ; used in 16 bit multiplication routine
speed_const1    EQU     0x3C            ; used in 16 bit multiplication routine, 32 bit result 
speed_const2    EQU     0x3D            ; used in 16 bit multiplication routine, 32 bit result 
speed_const3    EQU     0x3E            ; used in 16 bit multiplication routine, 32 bit result 
multcounter     EQU     0x3F            ; used in 16 bit multiplication routine, 32 bit result 
rpmmax_low      EQU     0x40            ; maximum rpm rate, low byte, copied from eeprom
rpmmax_high     EQU     0x41            ; maximum rpm rate, high byte, copied from eeprom
rpmmax_0        EQU     0x42            ; maximum rpm rate time interval, least significant byte, used in comparison for shift light
rpmmax_1        EQU     0x43            ; maximum rpm rate time interval, more significant byte 1, used in comparison for shift light
rpmmax_2        EQU     0x44            ; maximum rpm rate time interval, more significant byte 2, used in comparison for shift light
rpmmax_3        EQU     0x45            ; maximum rpm rate time interval, most significant byte, used in comparison for shift light
ccp1interval0   EQU     0x46            ; temporary rpm interval storage
ccp1interval1   EQU     0x47            ; temporary rpm interval storage
ccp1interval2   EQU     0x48            ; temporary rpm interval storage
ccp1interval3   EQU     0x49            ; temporary rpm interval storage
ccp2interval0   EQU     0x4A            ; temporary speed interval storage
ccp2interval1   EQU     0x4B            ; temporary speed interval storage
ccp2interval2   EQU     0x4C            ; temporary speed interval storage
ccp2interval3   EQU     0x4D            ; temporary speed interval storage
intervalr0      EQU     0x4E            ; present timer1 value
intervalr1      EQU     0x4F            ; present timer1 value
intervalr2      EQU     0x50            ; present timer1 value
intervalr3      EQU     0x51            ; present timer1 value
intervals0      EQU     0x52            ; present timer1 value
intervals1      EQU     0x53            ; present timer1 value
intervals2      EQU     0x54            ; present timer1 value
intervals3      EQU     0x55            ; present timer1 value
lastintr0       EQU     0x56            ; interval between last two rpm pulses
lastintr1       EQU     0x57            ; interval between last two rpm pulses
lastintr2       EQU     0x58            ; interval between last two rpm pulses
lastintr3       EQU     0x59            ; interval between last two rpm pulses
lastints0       EQU     0x5A            ; interval between last two speed pulses
lastints1       EQU     0x5B            ; interval between last two speed pulses
lastints2       EQU     0x5C            ; interval between last two speed pulses
lastints3       EQU     0x5D            ; interval between last two speed pulses
pulses          EQU     0x5E            ; number of pulses per crankshaft revolution
                                        ; not used
                                        ; not used
                                        ; not used
clockaddr       EQU     0x66            ; holds address for static ram and control byte operations in IIC clock chip 
clockdata       EQU     0x67            ; temporary storage for data for static ram operations
seconds         EQU     0x68            ; buffer for IIC clock chip settings
minutes         EQU     0x69            ;
hours           EQU     0x6A            ;
day             EQU     0x6B            ;
date            EQU     0x6C            ;
month           EQU     0x6D            ;
year            EQU     0x6E            ;
clockctrl       EQU     0x6F            ; settings byte in clock chip

; variables used in bank 3 (Hex 90-EF)

tablepointer    EQU     0x90            ; used in table lookups
logcounter      EQU     0x91            ; counter used during logger, increased every 1/32 of a second
freq_rpm        EQU     0x92            ; setting for log frequency of rpm channel
freq_speed      EQU     0x93            ; 
freq_lambda     EQU     0x94            ; 
freq_voltage    EQU     0x95            ; 
freq_tc         EQU     0x96            ; 
freq_air        EQU     0x97            ; 
freq_water      EQU     0x98            ; 
freq_throttle   EQU     0x99            ; 
freq_long       EQU     0x9A            ; 
freq_lat        EQU     0x9B            ; 
freq_mark       EQU     0x9C            ; 
freq_brake      EQU     0x9D            ; 
num_records     EQU     0x9E            ; counter for number of records
current_rec     EQU     0x9F            ; select record for downloading
rec_loopcntr    EQU     0xA0            ; byte used in addition loop
pointer_low     EQU     0xA1            ; pointer to record data in external memory
pointer_high    EQU     0xA2            ;
pointer_chip    EQU     0xA3            ;
endpoint_low    EQU     0xA4            ; pointer to end of record data in external memory
endpoint_high   EQU     0xA5            ;
endpoint_chip   EQU     0xA6            ;
recsizea0       EQU     0xA7            ; registers used in record size calculation
recsizea1       EQU     0xA8            ;
recsizea2       EQU     0xA9            ;
recsizeb0       EQU     0xAA            ;
recsizeb1       EQU     0xAB            ;
recsizeb2       EQU     0xAC            ;


;---------------------------------------------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------------------------------------------
; Data EEPROM Contents (PIC16F877: 256 bytes)
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------


                org     h'2100'         ; start of eeprom data memory

                de      d'175'          ; 000 vehicle outer wheel circumference in millimeters (default 1967 mm), low byte 
                de      d'7'            ; 001 vehicle outer wheel circumference in millimeters, high byte
                de      d'255'          ; 002 maximum rpm rate (default 10000 rpm) [0..16383], low byte
                de      d'63'           ; 003 maximum rpm rate high byte
                de      d'2'            ; 004 pulses per revolution
                de      d'0'            ; 005
                de      d'0'            ; 006
                de      d'0'            ; 007
                de      d'0'            ; 008
                de      d'0'            ; 009
                de      d'0'            ; 010
                de      d'0'            ; 011
                de      d'0'            ; 012
                de      d'0'            ; 013
                de      d'0'            ; 014 
                de      d'0'            ; 015 error flags bits, any errors during logging are stored here
                de      b'11111111'     ; 016 rpm, channel logging frequency values 
                de      b'11111111'     ; 017 speed                                     logrates (see also 'logdata' routine):
                de      b'11111111'     ; 018 lambda
                de      b'11111111'     ; 019 voltage                                   00000000         32 Hz
                de      b'11111111'     ; 020 thermocouple                              00000001         16 Hz
                de      b'11111111'     ; 021 air temperature                           00000011          8 Hz
                de      b'11111111'     ; 022 water temperature                         00000111          4 Hz
                de      b'11111111'     ; 023 throttle                                  00001111          2 Hz
                de      b'11111111'     ; 024 long acceleration                         00011111          1 Hz or every second
                de      b'11111111'     ; 025 lat acceleration                          00111111        1/2 Hz or every 2 seconds
                de      b'11111111'     ; 026 mark switch                               01111111        1/4 Hz or every 4 seconds
                de      b'11111111'     ; 027 brake switch                              11111111        never
                de      d'0'            ; 028
                de      d'0'            ; 029
                de      d'0'            ; 030
                de      d'0'            ; 031
                de      d'0'            ; 032
                de      d'0'            ; 033
                de      d'0'            ; 034
                de      d'0'            ; 035
                de      d'0'            ; 036
                de      d'0'            ; 037
                de      d'0'            ; 038
                de      d'0'            ; 039
                de      d'0'            ; 040
                de      d'0'            ; 041
                de      d'0'            ; 042
                de      d'0'            ; 043
                de      d'0'            ; 044
                de      d'0'            ; 045
                de      d'0'            ; 046
                de      d'0'            ; 047
                de      d'0'            ; 048
                de      d'0'            ; 049
                de      d'0'            ; 050
                de      d'0'            ; 051
                de      d'0'            ; 052
                de      d'0'            ; 053
                de      b'1'            ; 054 record incremental number
                de      b'0'            ; 055 record incremental number

                de      d'0'            ; 1, 056 start location of table of contents for data records
                de      d'0'            ;
                de      d'0'            ;
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; all bytes clear because nothing been recorded yet     
                de      d'0'            ; 2     
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 3     
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 4     
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 5     
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 6     
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 7     
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 8     
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 9     
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 10    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 11
                de      d'0'            ;
                de      d'0'            ;
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 12    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 13    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 14    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 15    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 16    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 17    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 18    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 19    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ; 20    
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       
                de      d'0'            ;       



;######################################################################################################################################
; Start of program code
;######################################################################################################################################


;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------



                org     h'0000'         ; tell the assembler to place following program code at the processor reset vector,
                                        ; which is the start of program memory page 0
                                        ; this code executes when a reset occurs

                                        ; Note: The contents of the PCLATH register are unchanged after a RETURN or RETFIE instruction
                                        ; is executed ! The user must rewrite the contents of the PCLATH register for any subsequent
                                        ; subroutine calls or goto instructions (meaning: make sure page bits are set properly !)



RESET           bcf     pclath,3        ; select program memory page 0 (hex 0000 up to hex 07FF)
                bcf     pclath,4        ; select program memory page 0 (hex 0000 up to hex 07FF)
                clrf    status          ; ensure bank bits are clear
                goto    INITIALIZE      ; skip interrupt routine and subroutines and go to beginning of program



;--------------------------------------------------------------------------------------------------------------------------------------


                org     h'0004'         ; tell the assembler to place following program code at interrupt vector location
                                        ; this code executes when an interrupt occurs


INTERRUPT       movwf   w_temp          ; save off current w register contents (will be stored in any bank !)
                swapf   status,w        ; swap status to be saved into w
                movwf   status_temp     ; save status register to status_temp register in bank 0
                clrf    status          ; bank 0, regardless of current bank, clears irp,rp1,rp0 (for other than 16F877)
                movf    fsr,w           ; save fsr register
                movwf   fsr_temp        ;
                movf    pclath,w        ; only required if using pages 1,2 and/or 3
                movwf   pclath_temp     ; save pclath
                clrf    pclath          ; select page zero, regardless of current page          

                                        ; see what caused the interrupt and act upon it:

                                        ; page selection should not change in interrupt routines


                bsf     flags2,norpm    ; assume we will not get rpm or speed pulses
                bsf     flags2,nospeed  ; flag will be reset in ccp1 and ccp2 interrupt code

                bank0                   ; test if the CCP1 module did a capture, if so, go calculate engine rpm pulse interval, but
                btfsc   pir1,ccp1if     ; only if we can use the present timer 1 values as we may get ccp and timer 1 irq's
                call    INT_CCP1        ; at the same time ! else we wait until the timer 1 value has been increased, bank0 return

                bank0                   ; test if the CCP2 module did a capture, if so, go calculate speed pulse interval, but
                btfsc   pir2,ccp2if     ; only if we can use the present timer 1 values as we may get ccp and timer 1 irq's
                call    INT_CCP2        ; at the same time ! else we wait until the timer 1 value has been increased

                bank0
                btfsc   pir1,tmr1if     ; test if timer 1 (which has 16 bits) has overflowed from 65535 to 0
                call    INT_TIMER1      ; if so, go increase the two extra counter bytes we use to extend the range, bank0 return

                bank0                   ;
                btfsc   pir1,rcif       ; test if UART has received a byte from a connected computer through the RS232
                call    INT_RX          ; if so, store valid bytes in buffer until command is complete, then set command flag

                call    INT_SHIFT       ; when calculated rpm rate is more than the given maximum turn on shift light, bank0 return

                                        ; done handling interrupts

INT_RESTORE     bank0                   ; make sure we are in the right bank (for other than 16F877)
                movf    pclath_temp,w   ; restore the register contents to their values from before the interrupt
                movwf   pclath          ; restore pclath
                movf    fsr_temp,w      ;
                movwf   fsr             ; restore fsr
                swapf   status_temp,w   ; swap status_temp register into w (sets bank to original state)
                movwf   status          ; restore pre-isr status register contents
                swapf   w_temp,f        ;
                swapf   w_temp,w        ; restore pre-isr w register contents
                retfie                  ; return from interrupt, re-enable interrupts


;--------------------------------------------------------------------------------------------------------------------------------------
; Handle Timer1 overflow interrupt
;--------------------------------------------------------------------------------------------------------------------------------------

INT_TIMER1      bank2                   ;
                incf    timer1y,f       ; increase the lower of two bytes which are used to extend the range of timer 1 to a
                skpnz                   ; total of four bytes, did this byte roll over from 255 to 0 ?
                incf    timer1z,f       ; yes, also increase the upper of the two bytes

                btfsc   flags2,norpm    ; did we get a rpm pulse ?
                call    INT_DROP_RPM    ; no, calculate a dropping rpm rate, bank2 return

                btfsc   flags2,nospeed  ; did we get a speed pulse ?
                call    INT_DROP_SPEED  ; no, calculate a dropping speed value, bank2 return

INT_TIMER1_SL   movlw   b'11111111'     ; now see about the shift light, this is value for all leds on
                btfsc   timer1y,0       ; we use this timer bit to flash the shift light as a stroboscope, is this bit high ?
                btfss   flags2,shlight  ; yes, so should we turn on the shift light ?
                clrw                    ; no, value for all leds off
                bank20                  ; 
                movwf   portd           ; yes, turn shift light on
                bcf     pir1,tmr1if     ; clear timer 1 receive interrupt flag bit
                return                  ; done, return in bank0


;--------------------------------------------------------------------------------------------------------------------------------------
; Handle CCP1 capture interrupt
;--------------------------------------------------------------------------------------------------------------------------------------

INT_CCP1                                ; since we can have both timer 1 overflow and ccp interrupt request at the same
                                        ; time it is important to update the timer 1 extension bytes at the right time,
                                        ; now see if we can use the present timer 1 values to calculate the engine RPM rate
                                        ; from CCP module 1 using the time (t) between two captures, otherwise we exit this
                                        ; routine and come back later when timer 1 has been incremented

                                        ; here we calculate the interval given by the rpm rate and use it to turn on or off the
                                        ; the so called 'shift light' to let the driver know it is time to shift gears,
                                        ; we do not want to calculate the actual RPM value on every interrupt since the division
                                        ; routine takes up too much processor time, the actual rpm calculation in done during logging
                                        ; or command execution

                                        ; engine RPM rate calculation:

                                        ; F [Hz]     =        1             /  t [s]
                                        ; RPM        =       60             /  t [s]
                                        ; RPM        = 60 * timer1clockrate /  interval [timer cycles]
                                        ; RPM        = (60 * 3686400/4) Mhz /  interval [instruction cycles]
                                        ; RPM        =    55,296,000        /  interval [instruction cycles]
                                        ; RPM        =    55,296,000        /  (capturevalue - previouscapturevalue)
                                        ; RPM        =    55,296,000        /  (new1cap - old1cap)
                                        ; quotient   =     numerator        /  denominator

                                        ; calculate interval (32 bits) and store result in denom:
                                        ; current  =    timer1z         timer1y         ccpr1h          ccpr1l
                                        ; new      =    new1cap3        new1cap2        new1cap1        new1cap0
                                        ; old      =    old1cap3        old1cap2        old1cap1        old1cap0
                                        ; interval =    denom_r3        denom_r2        denom_r1        denom_r0

                                        ; see also CALC_RPM and COPY_RPM routines

                bcf     flags2,norpm    ; we did get a rpm pulse

INT_CCP1_STORE  bank02                  ; yes, continue and handle CCP1 interrupt request
                movf    new1cap0,w      ; store previous captured values
                movwf   old1cap0        ;
                movf    new1cap1,w      ;
                movwf   old1cap1        ;
                movf    new1cap2,w      ;
                movwf   old1cap2        ;
                movf    new1cap3,w      ;
                movwf   old1cap3        ;

INT_CCP1_CAPT   bank20                  ; get capture moment
                movf    ccpr1l,w        ; get first captured byte
                bank02                  ;
                movwf   new1cap0        ;
                movwf   ccp1interval0   ;
                bank20                  ;
                movf    ccpr1h,w        ; get second captured byte
                bank02                  ;
                movwf   new1cap1        ;
                movwf   ccp1interval1   ;
                movf    timer1y,w       ; get third timer byte
                movwf   new1cap2        ;
                movwf   ccp1interval2   ;
                movf    timer1z,w       ; get newly capture values, first get the fourth timer byte
                movwf   new1cap3        ;
                movwf   ccp1interval3   ;

INT_CCP1_CHECK  bank20
                btfss   pir1,tmr1if     ; do we also have a timer 1 overflow interrupt request ?
                goto    INT_CCP1_SUB    ; no, so it is ok to handle the CCP1 interrupt request
                movlw   d'128'          ; yes, now see if we can use the current timer 1 value (use halfway value)
                subwf   ccpr1h,w        ; any capture from (long) before the timer 1 overflow must use old timer values
                skpnc                   ; was the capture from before the timer 1 overflow ?
                goto    INT_CCP1_SUB    ; yes,
                bank02                  ;
                incf    ccp1interval2,f ; no, increase timer 1 bytes 3 and 4
                incf    new1cap2,f      ;
                skpnz                   ;
                incf    ccp1interval3,f ;
                movf    ccp1interval3,w ;
                movwf   new1cap3        ;

INT_CCP1_SUB    bank02                  ;
                movf    old1cap0,w      ; subtraction, calculate the actual interval
                subwf   ccp1interval0,f ; store interval byte 0
                movf    old1cap1,w      ;
                skpc                    ;
                incfsz  old1cap1,w      ;
                subwf   ccp1interval1,f ;
                movf    old1cap2,w      ;
                skpc                    ;
                incfsz  old1cap2,w      ;
                subwf   ccp1interval2,f ;
                movf    old1cap3,w      ;
                skpc                    ;
                incfsz  old1cap3,w      ;
                subwf   ccp1interval3,f ;

INT_CCP1_LAST   movf    ccp1interval0,w ; copy time interval between last two pulses
                movwf   lastintr0       ;
                movf    ccp1interval1,w ;
                movwf   lastintr1       ;
                movf    ccp1interval2,w ;
                movwf   lastintr2       ;
                movf    ccp1interval3,w ;
                movwf   lastintr3       ;

INT_CCP1_DONE   bank20                  ;
                bcf     pir1,ccp1if     ; clear capture 1 interrupt flag
                return                  ; we're done here, return in bank2


;--------------------------------------------------------------------------------------------------------------------------------------
; Handle CCP2 capture interrupt
;--------------------------------------------------------------------------------------------------------------------------------------

INT_CCP2                                ; since we can have both timer 1 overflow and ccp interrupt request at the same
                                        ; time it is important to update the timer 1 extension bytes at the right time,
                                        ; now see if we can use the present timer 1 values to calculate the vehicle speed
                                        ; from CCP module 2 using the time (t) between two captures, otherwise we exit this
                                        ; routine and come back later when timer 1 has been incremented

                bcf     flags2,nospeed  ; we did get a speed pulse

INT_CCP2_STORE  bank02                  ; yes,
                movf    new2cap0,w      ; store previous captured values
                movwf   old2cap0        ;
                movf    new2cap1,w      ;
                movwf   old2cap1        ;
                movf    new2cap2,w      ;
                movwf   old2cap2        ;
                movf    new2cap3,w      ;
                movwf   old2cap3        ;

INT_CCP2_CAPT   bank20                  ; get capture moment
                movf    ccpr2l,w        ; get first captured byte
                bank02                  ;
                movwf   new2cap0        ;
                movwf   ccp2interval0   ;
                bank20                  ;
                movf    ccpr2h,w        ; get second captured byte
                bank02                  ;
                movwf   new2cap1        ;
                movwf   ccp2interval1   ;
                movf    timer1y,w       ; get third timer byte
                movwf   new2cap2        ;
                movwf   ccp2interval2   ;
                movf    timer1z,w       ; get newly capture values, first get the fourth timer byte
                movwf   new2cap3        ;
                movwf   ccp2interval3   ;

INT_CCP2_CHECK  bank20
                btfss   pir1,tmr1if     ; do we also have a timer 1 overflow interrupt request ?
                goto    INT_CCP2_SUB    ; no, so it is ok to handle the CCP2 interrupt request
                movlw   d'128'          ; yes, now see if we can use the current timer 1 value (use halfway value)
                subwf   ccpr2h,w        ; any capture from (long) before the timer 1 overflow must use old timer values
                skpnc                   ; was the capture from before the timer 1 overflow ?
                goto    INT_CCP2_SUB    ; yes,
                bank02                  ;
                incf    ccp2interval2,f ; no, increase timer 1 bytes 3 and 4
                incf    new2cap2,f      ;
                skpnz                   ;
                incf    ccp2interval3,f ;
                movf    ccp2interval3,w ;
                movwf   new2cap3        ;

INT_CCP2_SUB    bank02                  ;
                movf    old2cap0,w      ; subtraction, the resulting interval is put in denominator
                subwf   ccp2interval0,f ; store interval byte 0 for use in division routine
                movf    old2cap1,w      ;
                skpc                    ; is capture0 - hold0 < 0 ?
                incfsz  old2cap1,w      ; yes, 'borrow' from more significant byte
                subwf   ccp2interval1,f ; no, borrow has been skipped, do subtraction and store for use in division
                movf    old2cap2,w      ;
                skpc                    ;
                incfsz  old2cap2,w      ;
                subwf   ccp2interval2,f ;
                movf    old2cap3,w      ;
                skpc                    ;
                incfsz  old2cap3,w      ;
                subwf   ccp2interval3,f ;

INT_CCP2_LAST   movf    ccp2interval0,w ; copy time interval between last two pulses
                movwf   lastints0       ;
                movf    ccp2interval1,w ;
                movwf   lastints1       ;
                movf    ccp2interval2,w ;
                movwf   lastints2       ;
                movf    ccp2interval3,w ;
                movwf   lastints3       ;

INT_CCP2_DONE   bank20                  ;
                bcf     pir2,ccp2if     ; clear capture 2 interrupt flag
                return                  ; done, return in bank0


;--------------------------------------------------------------------------------------------------------------------------------------

INT_DROP_RPM                            ; Drop the speed value (calculate pulse interval) in case we do not get speed pulses anymore


INT_DROP_R_NOW  bank2                   ;
                clrf    intervalr0      ; get present timer1 value (now)
                clrf    intervalr1      ; at the moment of overflow these values became zero
                movf    timer1y,w       ; get third timer byte
                movwf   intervalr2      ;
                movf    timer1z,w       ;
                movwf   intervalr3      ; store in rpm interval

INT_DROP_R_SUB  movf    new1cap0,w      ; calculate rpm value when we do not have any rpm pulses on the input
                subwf   intervalr0,f    ; subtraction, calculate the time interval between the last pulse and now
                movf    new1cap1,w      ; store the result in intervalr0-3
                skpc                    ;
                incfsz  new1cap1,w      ;
                subwf   intervalr1,f    ;
                movf    new1cap2,w      ;
                skpc                    ;
                incfsz  new1cap2,w      ;
                subwf   intervalr2,f    ;
                movf    new1cap3,w      ;
                skpc                    ;
                incfsz  new1cap3,w      ;
                subwf   intervalr3,f    ;

INT_DROP_R_TEST movf    intervalr0,w    ; is the time interval between the last pulse and now larger than the time between
                subwf   lastintr0,w     ; the last two pulses ? 
                movf    intervalr1,w    ;
                skpc                    ;
                incfsz  intervalr1,w    ;
                subwf   lastintr1,w     ;
                movf    intervalr2,w    ;
                skpc                    ;
                incfsz  intervalr2,w    ;
                subwf   lastintr2,w     ;
                movf    intervalr3,w    ;
                skpc                    ;
                incfsz  intervalr3,w    ;
                subwf   lastintr3,w     ;
                skpnc                   ;
                return                  ; no, do nothing 

INT_DROP_R_YES  movf    intervalr0,w    ; copy the new (longer) interval value so we drop the rpm rate
                movwf   ccp1interval0   ;
                movf    intervalr1,w    ;
                movwf   ccp1interval1   ;
                movf    intervalr2,w    ;
                movwf   ccp1interval2   ;
                movf    intervalr3,w    ;
                movwf   ccp1interval3   ;

INT_DROP_R_OVR  movlw   d'255'          ; timer1 and the extra bytes will overflow, this will affect the interval value indirectly
                subwf   ccp1interval3,w ; since the new1cap values will stay the same, so we have to adjust the new1cap values
                skpz                    ; is the value of the interval about to overflow ?
                return                  ; no, done
                rlf     new1cap3,f      ; yes, get the most significant bit of new1cap
                skpc                    ; toggle it's value
                setc                    ; this will move the new1cap value in time
                skpnc                   ; so the interval value will be decreased but still very long to show zero rpm
                clrc                    ;
                rrf     new1cap3,f      ;
                return                  ; done

;--------------------------------------------------------------------------------------------------------------------------------------
; See if we are getting input pulses of rpm and speed, otherwise calculate dropping rpm and speed values
;--------------------------------------------------------------------------------------------------------------------------------------

INT_DROP_SPEED                          ; calculate dropping rpm and speed values
                                        ; Drop the rpm rate (calculate new rpm pulse interval) in case we do not get rpm pulses anymore


INT_DROP_S_NOW  bank2                   ;
                clrf    intervals0      ; get present timer1 value (now)
                clrf    intervals1      ; at the moment of overflow these values became zero
                movf    timer1y,w       ; get third timer byte
                movwf   intervals2      ;
                movf    timer1z,w       ; 
                movwf   intervals3      ; store in speed interval

INT_DROP_S_SUB  movf    new2cap0,w      ; subtraction, calculate the time interval between the last pulse and now
                subwf   intervals0,f    ; store the result in intervals0-3
                movf    new2cap1,w      ;
                skpc                    ;
                incfsz  new2cap1,w      ;
                subwf   intervals1,f    ;
                movf    new2cap2,w      ;
                skpc                    ;
                incfsz  new2cap2,w      ;
                subwf   intervals2,f    ;
                movf    new2cap3,w      ;
                skpc                    ;
                incfsz  new2cap3,w      ;
                subwf   intervals3,f    ;

INT_DROP_S_TEST movf    intervals0,w    ; is the time interval between the last pulse and now larger than the time between
                subwf   lastints0,w     ; the last two pulses ? 
                movf    intervals1,w    ;
                skpc                    ;
                incfsz  intervals1,w    ;
                subwf   lastints1,w     ;
                movf    intervals2,w    ;
                skpc                    ;
                incfsz  intervals2,w    ;
                subwf   lastints2,w     ;
                movf    intervals3,w    ;
                skpc                    ;
                incfsz  intervals3,w    ;
                subwf   lastints3,w     ;
                skpnc                   ;
                return                  ; no, do nothing 

INT_DROP_S_YES  movf    intervals0,w    ; yes, copy the new (longer) interval value so we drop the speed value
                movwf   ccp2interval0   ;
                movf    intervals1,w    ;
                movwf   ccp2interval1   ;
                movf    intervals2,w    ;
                movwf   ccp2interval2   ;
                movf    intervals3,w    ;
                movwf   ccp2interval3   ;

INT_DROP_S_OVR  movlw   d'255'          ; timer1 and the extra bytes will overflow, this will affect the interval value indirectly
                subwf   ccp2interval3,w ; since the new2cap values will stay the same, so we have to adjust the new2cap values
                skpz                    ; is the value of the interval about to overflow ?
                return                  ; no, done
                rlf     new2cap3,f      ; yes, get the most significant bit of new1cap
                skpc                    ; toggle it's value
                setc                    ; this will move the new2cap value in time
                skpnc                   ; so the interval value will be decreased but still very long to show zero speed value
                clrc                    ;
                rrf     new2cap3,f      ;
                return                  ; done


;--------------------------------------------------------------------------------------------------------------------------------------

INT_SHIFT                               ; when the calculated rpm value is more than the given maximum turn on the shift light 

                                        ; t [s]                                 =       1               /  F [Hz]
                                        ; t [s]                                 =       60              / RPM
                                        ; interval [timer cycles]               = 60 * timer1clockrate  / RPM
                                        ; interval [instruction cycles]         = (60 * 3686400/4) MHz  / RPM
                                        ; interval [instruction cycles]         =     55,296,000        / RPM
                                        ; (capturevalue - previouscapturevalue) =    55,296,000         / RPM
                                        ; (new1cap - old1cap)                   =    55,296,000         / RPM
                                        ; denominator1 (from ccp1 routine)      =    55,296,000         / RPM
                                        ; so we want to compare the denominator1 value with the four interval bytes:
                                        ; interval   :  rpmmax_3        rpmmax_2        rpmmax_1        rpmmax_0
                                        ; which we have calculated at initialization from the two maximum rpm bytes
                                        ; rpm max    :  rpmmax_high     rpmmax_low

                bank2                   ;
                movf    ccp1interval0,w ; subtraction, when the measured rpm value is more than the set maximum we should turn on the
                subwf   rpmmax_0,w      ; so called 'shift light' to let the driver know it is time to shift gears
                movf    ccp1interval1,w ; so turn on if: rpmmax - denominator (=interval) > 0
                skpc                    ; is capture0 - hold0 < 0 ?
                incfsz  ccp1interval1,w ; yes, 'borrow' from more significant byte
                subwf   rpmmax_1,w      ; no, borrow has been skipped, do subtraction
                movf    ccp1interval2,w ;
                skpc                    ;
                incfsz  ccp1interval2,w ;
                subwf   rpmmax_2,w      ;
                movf    ccp1interval3,w ;
                skpc                    ;
                incfsz  ccp1interval3,w ;
                subwf   rpmmax_3,w      ;
                bank20                  ;
                skpc                    ; should we turn off the shift light ?
INT_SHIFT_OFF   bcf     flags2,shlight  ; yes, rpm rate is smaller than given maximum, turn shift light (flag) off, flag used in timer
                skpc                    ; again, should we turn off the shift light ?
                clrf    portd           ; yes, turn shift light off
                skpnc                   ; should we turn on the shift light ?
INT_SHIFT_ON    bsf     flags2,shlight  ; yes, with this high rpm rate, turn shift light flag on, used in timer interrupt to flash light
                return                  ; done, return in bank0


;--------------------------------------------------------------------------------------------------------------------------------------
; Handle UART receive interrupt
;--------------------------------------------------------------------------------------------------------------------------------------


                                        ; input bytes are stored in buffer for later use when the received command is going to be
                                        ; interpreted and executed
                                        ; check for command byte sequence, if all bytes of a command have been received set the flag
                                        ; bit that allows the command execute routine

                                        ; a valid command byte sequence is (in ascii):
        
                                        ; <STX>         1 byte indicator for start of command, value hex 02
                                        ; <c1>          1 byte, first letter of command
                                        ; <c2>          1 byte, second letter of command
                                        ; <data bytes>  0-64 data bytes, all values accepted except ascii codes STX and ETX 
                                        ; <ETX>         1 byte indicator for end of command, value hex 03
                                        ; <checksum>    1 byte checksum, any hex value 00-FF (incl.hex 03 !), last byte of command

                                        ; the checksum byte value makes the sum of all bytes from <STX> up to and including <ETX> zero

                                        ; used bits for uart in flags register:
                                        ; bit 0 = high : STX received, started reception of rest of command
                                        ; bit 1 = high : ETX received, now wait for checksum
                                        ; bit 2 = high : command has data bytes
                                        ; bit 3 = high : all bytes received, now ready for execution of command

INT_RX          bank0
                bsf     portc,cts_out   ; clear CTS to hold data stream from computer
                btfsc   rcsta,oerr      ; overrun error ?
                goto    OVERFLOW_ERR    ; yes, we cannot keep up with the input, go discard input
                btfsc   rcsta,ferr      ; no, framing error ?
                goto    FRAMING_ERR     ; yes, go discard input
                movlw   b'00000001'     ; no, select rx9d/parity bit (we use odd parity)
                andwf   rcsta,w         ; store only the parity bit in w register
                movwf   rx_parity       ; copy the value to rx_parity
                movf    rcreg,w         ; deque received byte           
                movwf   rx_byte         ; store byte for later use
CHECK_PARITY    xorwf   rx_parity,f     ; use parity bit in calculation
                swapf   rx_parity,w     ; use w and rx_parity register for the calculation
                xorwf   rx_parity,f     ; calculate nibbles
                rrf     rx_parity,w     ;
                xorwf   rx_parity,f     ; at this point the parity values of the nibbles are in bit 2 and bit 0
                btfss   rx_parity,2     ; if parity one nibble is 1 then whole byte parity equals that of other nibble, skip ahead
                incf    rx_parity,f     ; otherwise, invert bit 0
                btfsc   rx_parity,0     ; parity error ?
                goto    PARITY_ERR      ; yes
                btfsc   flags1,command  ; no, did we already receive a command or are we busy executing one ?
                return                  ; yes, discard input,seems handshaking is ignored, but we cannot accept a new command right now 
VALID_BYTE      movf    rx_byte,w       ; if we get here we have received valid input
                sublw   h'02'           ; ascii code for 'start of text' (STX)
                skpnz                   ; check if byte matches
                goto    COMMAND_START   ; match, go set flags to allow reception of rest of command
STX?            btfss   flags1,stx      ; no match, but did we already receive the STX byte ?
                goto    NO_ERR          ; no, do not use any received byte until STX has been received first
ADD_TO_CS       movf    rx_byte,w       ; yes, received byte is in rx_byte, what was it again ?
                addwf   rx_checksum,f   ; use all bytes including the checksum byte in the checksum calculation
CHECKSUM?       btfsc   flags1,etx      ; did we already receive all bytes of the command and is this the checksum byte ?
                goto    CHECK_SUM       ; yes, go test checksum
ETX?            sublw   h'03'           ; ascii code for 'end of text' (ETX)
                skpnz                   ; did we receive (ETX) and therefore all command and data bytes ?
                goto    GOT_ETX         ; yes, go set flag and exit
OVERFLOW?       movlw   rx_buffer66 + 1 ; no, check if pointer has passed the maximum allowed address
                subwf   rx_pointer,w    ; see where the pointer is
                skpnz                   ; already there ?
                goto    ABORT_COMMAND   ; yes, too many data bytes: abort receiving this command
STORE           movf    rx_pointer,w    ; get the available address for storage of the newly received data byte
                movwf   rx_maxpos       ; store the pointer, keep the position of the last data byte
                movwf   fsr             ; use value to set the indirect file address
                bcf     status,irp      ; make sure upper bit in address is zero (select register bank 0 and 1)
                movf    rx_byte,w       ; what was the received byte again ?
                movwf   indf            ; store it in the command input buffer
INC_POINTER     incf    rx_pointer,f    ; go point to next position for any following byte
                goto    NO_ERR          ; done handling the received byte, return in bank0


COMMAND_START   movlw   h'02'           ; the value of STX should be
                movwf   rx_checksum     ; the initial checksum value
                movlw   rx_buffer00     ; first position in the input buffer for command bytes
                movwf   rx_pointer      ; use this value to reset pointer
                movwf   rx_maxpos       ; reset the pointer to the last received data byte
                bsf     flags1,stx      ; set flag 'STX has been received, wait for the rest of the command bytes'
                bcf     flags1,withdata ; clear flag for data bytes
                goto    NO_ERR          ; done handling the received byte, return in bank0


CHECK_SUM       movf    rx_checksum,w   ; the checksum is the sum of all bytes added together and should be zero
                skpz                    ; is checksum ok ?
                goto    ABORT_COMMAND   ; no, go abort the reception of the command
CHECK_SIZE      movlw   rx_buffer01     ; yes, second position in buffer
                subwf   rx_maxpos,w     ; test if the last command byte has been placed at the second place or further,
                skpc                    ; so did we get at least two command bytes ?
                goto    ABORT_COMMAND   ; no, go abort the reception of the command
                skpz                    ; did we get more than three bytes ?
                bsf     flags1,withdata ; yes, set flag 'this is a command with data'
                bsf     flags1,command  ; set flag to get command executed
                bcf     flags1,stx      ; now ok to reset stx flag for a next command
                bcf     flags1,etx      ; now ok to reset etx flag for a next command
                return                  ; bytes have been handled, return without setting CTS to keep data stream blocked, bank0 return 


GOT_ETX         bsf     flags1,etx      ; we have received all command and data bytes, now wait for the checksum byte
                bcf     portc,cts_out   ; set CTS to allow computer to send data
                return                  ; done
                

ABORT_COMMAND   bcf     flags1,stx      ; abort receiving command,
                bcf     flags1,etx      ; clear any command bytes received up to now 
                movlw   h'15'           ; negative acknowledgement (NAK)
                pagesel TX_BYTE         ; make right page selection before call
                call    TX_BYTE         ; send 'command has NOT been accepted', return in bank0
                pagesel NO_ERR          ; make right page selection before call
                goto    NO_ERR          ; done handling the received byte, return in bank0


OVERFLOW_ERR    movf    rcreg,w         ; deque byte (discard input byte)
                movf    rcreg,w         ; deque byte (discard input byte)
                bcf     rcsta,cren      ; clear overrun error bit
                bsf     rcsta,cren      ; re-enable receive
                bcf     flags1,stx      ; clear any command bytes received up to now
                bcf     flags1,etx      ; clear any command bytes received up to now
                bcf     portc,cts_out   ; set CTS to allow computer to send data
                return                  ; done


FRAMING_ERR     movf    rcreg,w         ; we did not deque received byte yet, do so now (discard input byte), update receive flags
PARITY_ERR      bcf     flags1,stx      ; clear any command bytes received up to now
                bcf     flags1,etx      ; clear any command bytes received up to now
NO_ERR          bcf     portc,cts_out   ; set CTS to allow computer to send data
                return                  ; done


;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
;               Start of main program
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------


INITIALIZE

INIT_INT_OFF    clrf    intcon          ; ensure there are no interrupt requests
                bank1                   ;
                clrf    pie1            ; disable all pheripheral interrupts
                clrf    pie2            ;


INIT_CHECKPIC                           ; ******* check pic integrity: checksum pic program memory, set/reset sram bits


INIT_PORTA      bank0                   ; port A will have five analog inputs (note: port A is set as ANALOG i/o as default)
                clrf    porta           ; clear output data latches
                bank1                   ;
                movlw   b'00111111'     ;
                movwf   trisa           ; make all six pins of port A inputs
                                        ; Port A0 = Input, Lambda sensor
                                        ; Port A1 = Input, Voltage In
                                        ; Port A2 = Input, Thermocouple
                                        ; Port A3 = Input, Air Temperature
                                        ; Port A4 = Input, clock input 32768 Hz from clock timer chip
                                        ; Port A5 = Input, Water Temperature


INIT_PORTB      bank0                   ; port B
                clrf    portb           ; clear output data latches, make all outputs low
                bank1                   ;
                movlw   b'00111111'     ;       
                movwf   trisb           ; make port B input & outputs
                                        ; Port B0 = Input Run
                                        ; Port B1 = Input _Mark_
                                        ; Port B2 = Input Brake
                                        ; Port B3 = Input Laptime
                                        ; Port B4 = Input _TC disconnected_
                                        ; Port B5 = Input BoardsupplyOn
                                        ; Port B6 = Output Status Led Red       
                                        ; Port B7 = Output Status Led Green

INIT_PORTC      bank0                   ; inputs, communications port to computer (RS232) realtime clock and serial eeprom (IIC)
                clrf    portc           ; clear output data latches
                bank1                   ;
                movlw   b'11111110'     ;       
                movwf   trisc           ; set port C configuration
                                        ; Port C0 = Output computer CTS
                                        ; Port C1 = Input Speed
                                        ; Port C2 = Input RPM
                                        ; Port C3 = Input* SDA for MSSP IIC, configure as input
                                        ; Port C4 = Input* SCL for MSSP IIC, configure as input
                                        ; Port C5 = Input computer RTS
                                        ; Port C6 = Output* computer RX, configure as input
                                        ; Port C7 = Input computer TX


INIT_PORTD      bank0                   ; port D will drive eight pairs of leds (rpm meter/shift light)
                clrf    portd           ; clear output data latches, all outputs low
                bank1                   ;
                movlw   b'00000000'     ;       
                movwf   trisd           ; make port D all outputs


INIT_PORTE      bank0                   ; port E will have three analog inputs (note: port E is set as ANALOG i/o as default)
                clrf    porte           ; clear output data latches
                bank1                   ;
                movlw   b'00000111'     ; make port E all inputs
                movwf   trise           ;
                                        ; Port E0 = Input Throttle
                                        ; Port E1 = Input XAccelleration
                                        ; Port E2 = Input YAccelleration


INIT_TIMER0     clrwdt                  ; clear watchdog timer to prevent unintended device reset
                movlw   b'10100001'     ; we want 32 Hz interrupts using the 32768 Hz output from clock chip, divide by 4,
                bank1                   ; assign prescaler to timer0, increment from external clock on low-to-high,
                movwf   optionreg       ; pull-ups on portb disabled, interrupt on falling edge of B0/int pin
                bcf     intcon,t0ie     ; disable interrupt from timer 0, it will be enabled only during logging


INIT_TIMER1     bank0                   ; use timer 1 as a timer/counter to count instruction cycles
                movlw   b'00000001'     ; use internal instruction cycle clock divided by 1, turn on timer
                movwf   t1con           ; configure timer 1
                clrf    tmr1l           ; start timer at zero value
                clrf    tmr1h           ; start timer at zero value
                bank1                   ;
                bsf     pie1,tmr1ie     ; enable interrupt from timer 1
        

INIT_TIMER2     bank0                   ;
                movlw   b'00000000'     ;
                movwf   t2con           ; disable timer 2


INIT_CCP1       bank1                   ;
                bsf     trisc,2         ; make sure pin rc2/ccp1 is configured as input
                bsf     pie1,ccp1ie     ; enable interupts from CCP1 module
                bank0                   ;
                movlw   b'00000101'     ; capture mode, on every rising edge of pin rc2/ccp1
                movwf   ccp1con         ;


INIT_CCP2       bank1                   ;
                bsf     trisc,1         ; make sure pin rc1/t1osi/ccp2 is configured as input
                bsf     pie2,ccp2ie     ; enable interrupt from CCP2 module
                bank0                   ;
                movlw   b'00000101'     ; capture mode, on every rising edge of pin rc1/t1osi/ccp2
                movwf   ccp2con         ;


INIT_IIC        bank1                   ; configure the MSSP Module as IIC bus, PIC is bus master
                bcf     pie1,sspie      ; disable interrupt from ssp action
                bcf     pie2,bclie      ; disable interrupt from ssp bus collision
                movlw   d'9'            ; baud rate calculation:  x = ((( Fosc / IICbaudrate)/4)-1) (~100 kHz @ 3.6864Mhz)
                movwf   sspadd          ; set IIC baud rate to 100 kHz
                bcf     sspstat,cke     ; select IIC input levels
                bsf     sspstat,smp     ; set slew rate for standard speed mode (100 kHz)
                bank0                   ;
                movlw   b'00101000'     ; enable serial port, master mode
                movwf   sspcon          ; configure
                pagesel IIC_START       ; make right program memory page selection
                call    IIC_START       ; send initialization sequence (see Microchip AN709): start, 9 ones, start, stop
                bank0                   ; to properly reset eeprom devices on iic bus (when circuit has been reset during write)
                movlw   b'11111111'     ; send 8 times 1
                movwf   sspbuf          ; send the slave address
                bank1                   ;
                pagesel INIT_IIC_LOOP1  ; make right program memory page selection
INIT_IIC_LOOP1  btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    INIT_IIC_LOOP1  ; no, wait here until it is completed
                bsf     sspcon2,ackdt   ; select NOACK
                bsf     sspcon2,acken   ; send NOACK, 9th one bit 
INIT_IIC_LOOP2  btfsc   sspcon2,acken   ; send completed ?
                goto    INIT_IIC_LOOP2  ; no, wait here until send has been completed
                pagesel IIC_START       ; make right program memory page selection
                call    IIC_START       ;
                call    IIC_STOP        ;
                bank1                   ;
                clrf    iicchip         ; select first eeprom chip


INIT_AD         bank1                   ;
                movlw   b'10000000'     ; right justified, five pins of port A and three pins of port E will be analog inputs,
                movwf   adcon1          ; note: more configuration is to be done in a/d subroutine
                bcf     pie1,adie       ; disable interrupt from a/d module


INIT_UART       bank1                   ; configure UART for asynchronous communications
                movlw   d'3'            ; value for baudrate (23:9600, 11:19200, 3:57600, 1:115200 baud Fosc=3.6864 MHz) 
                movwf   spbrg           ; set UART to this value
                movlw   b'01000110'     ; 9 bit, Tx off, async, high speed (transmit: 8 data bits, odd parity , 1 stop bit)
                movwf   txsta           ; load Tx status register
                bank0                   ;
                movlw   b'01010000'     ; serial port disabled, 9 bit, Rx off (receive: 8 data bits, odd parity, 1 stop bit)
                movwf   rcsta           ; load Rx status register
                bsf     rcsta,spen      ; enable reception
                bank1                   ;
                bsf     txsta,txen      ; enable transmission
                bsf     pie1,rcie       ; enable interrupt on receive
                bcf     portc,cts_out   ; set CTS to allow computer to send data


INIT_CLOCK      movlw   d'7'            ; location of clock control byte
                bank2                   ;
                movwf   clockaddr       ;
                movlw   b'10000011'     ; clock control byte, set square wave at 32768 Hz but still disabled and it's output high 
                call    IIC_WR_CLKCHIP  ; write byte to control bytes or static ram of clock chip, returns in bank1


INIT_EEPROM     nop                     ; ****** see if datalogger internal eeprom has ever been intialized, otherwise set defaults     



INIT_VARIABLES  clrf    flags1          ; clear flag register1
                clrf    flags2          ; clear flag register2
                bank2                   ;
                clrf    timer1y         ; range extension for timer1 byte three
                clrf    timer1z         ; range extension for timer1 byte four
                clrf    new1cap0        ; make sure the rpm and speed values start at zero when there are no input pulses
                clrf    new1cap1        ;
                clrf    new1cap2        ;
                clrf    new1cap3        ;
                clrf    new2cap0        ;
                clrf    new2cap1        ;
                clrf    new2cap2        ;
                clrf    new2cap3        ;
                clrf    lastintr0       ;
                clrf    lastintr1       ;
                clrf    lastintr2       ;
                clrf    lastintr3       ;
                clrf    lastints0       ;
                clrf    lastints1       ;
                clrf    lastints2       ;
                clrf    lastints3       ;
                bank3                   ;
                clrf    current_rec     ; no record selected for download
                pagesel COPY_RPMVALUES  ; make right program memory page selection
                call    COPY_RPMVALUES  ; call all routines seperately because they have all returns
                call    COPY_WHEELC     ; to be able to use them individually, return with page setting...
                call    COPY_LOGRATES   ; 
                pagesel COPY_ERRORFLAGS ; restore program memory page selection
                call    COPY_ERRORFLAGS ; get values maximum rpm rate, wheel circumference, lograte values, error flag bits

INIT_INT_ON     bank0
                clrf    pir1            ; clear all pheripheral interrupt request flags
                clrf    pir2            ;
                bsf     intcon,peie     ; enable peripheral interrupts
                bsf     intcon,gie      ; enable all interrupts 


INIT_READY      bsf     portb,led_red   ; turn on red status led as indication power on and software initialized

INIT_RUN                                ; empty line to avoid mplab error
                pagesel MAIN            ; make right program memory page selection
                goto    MAIN            ; all initialization has been done, go run main code


;--------------------------------------------------------------------------------------------------------------------------------------

COPY_RPMVALUES                          ; reads the maximum rpm value from pic eeprom addresses, from this value the interval is
                                        ; calculated that will be used to turn on the shift light when the measured rpm rate is
                                        ; larger than the given maximum
                                        ; calculation below is for one pulse per revolution
                                        ; a correction is done for two pulses per revolution

                                        ; engine RPM rate calculation, see also INT_CCP1
                                        ; F [Hz]     =        1             /  t [s]
                                        ; RPM        =       60             /  t [s]
                                        ; RPM        = 60 * timer1clockrate /  interval [timer cycles]
                                        ; RPM        = (60 * 3686400/4) Mhz /  interval [instruction cycles]
                                        ; RPM        =    55,296,000        /  interval [instruction cycles]
                                        ; RPM        =    55,296,000        /  (capturevalue - previouscapturevalue)
                                        ; RPM        =    55,296,000        /  (new1cap - old1cap)
                                        ; quotient   =     numerator        /  denominator

                                        ; see also CALC_RPM and COPY_RPM routines

                                        ; calculate interval (input values are 32 bits, the result is 32 bits):
                                        ; t [s]                                 =       1               /  F [Hz]
                                        ; t [s]                                 =       60              / RPMMAX
                                        ; interval [timer cycles]               = 60 * timer1clockrate  / RPMMAX
                                        ; interval [instruction cycles]         = (60 * 3686400/4) MHz  / RPMMAX
                                        ; interval [instruction cycles]         =     55,296,000        / RPMMAX
                                        ; interval [instruction cycles]         =    55,296,000         / RPMMAX
                                        ; interval [instruction cycles]         =    55,296,000         / RPMMAX
                                        ; interval [instruction cycles]         =    55,296,000         / RPMMAX
                                        ; rpmmax_3&2&1&0                        =    55,296,000         / rpmmax_high&low
                                        ; quotient                              =     numerator         / denominator

                                        ; numerator  :  nrator3         nrator2         nrator1         nrator0
                                        ; denominator:  denom_r3        denom_r2        denom_r1        denomr_0
                                        ; quotient   :  rpmmax_3        rpmmax_2        rpmmax_1        rpmmax_0
                                        ; remainder  :  remain3         remain2         remain1         remain0

                                        ; the remainder R is not used

                                        ; interval   :  rpmmax_3        rpmmax_2        rpmmax_1        rpmmax_0
                                        ; rpm max    :  rpmmax_high     rpmmax_low


COPY_PULSES     movlw   d'4'            ; pic eeprom address of low byte of rpm maximum
                pagesel IEE_READ        ; make right program memory page selection
                call    IEE_READ        ; read pic eeprom, address in w, data returned in w, returns in bank2
                movwf   pulses          ; store value
COPY_RPM_READ   movlw   d'2'            ; pic eeprom address of low byte of rpm maximum
                call    IEE_READ        ; read pic eeprom, address in w, data returned in w, returns in bank2
                movwf   rpmmax_low      ; store value, low byte
                movlw   d'3'            ; eeprom address of high byte of rpm maximum
                call    IEE_READ        ; read pic eeprom, address in w, data returned in w, returns in bank2
                movwf   rpmmax_high     ; store value, high byte
                pagesel COPY_RPM_DIV    ; make right program memory page selection
COPY_CORRECTION btfsc   pulses,0        ; do we have one pulse per revolution ?
                goto    COPY_RPM_DIV    ; yes, go use the value as it is
                clrc                    ; no, multiply the maxrpm value by two to correct for the number of pulses per revolution
                rlf     rpmmax_low      ; low byte
                rlf     rpmmax_high     ; high byte
COPY_RPM_DIV    bank2                   ;
                movlw   d'3'            ; set numerator X = 55,296,000 = 3 : 75 : 192 : 0
                movwf   nrator3         ;
                movlw   d'75'           ;
                movwf   nrator2         ;
                movlw   d'192'          ;
                movwf   nrator1         ;
                movlw   d'0'            ;
                movwf   nrator0         ;
                clrf    denom_r3        ; get denominator
                clrf    denom_r2        ;
                movf    rpmmax_high,w   ;
                movwf   denom_r1        ;
                movf    rpmmax_low,w    ;
                movwf   denom_r0        ;
                movlw   d'32'           ; there are 32 bits in this division
                movwf   divcounter      ;
                clrf    remain0         ; clear remainder
                clrf    remain1         ;
                clrf    remain2         ;
                clrf    remain3         ;
COPY_RPM_LOOP   clrc                    ;
                rlf     nrator0,f       ; shift next bit to remainder (msbit numerator to lsbit remainder)
                rlf     nrator1,f       ;
                rlf     nrator2,f       ;
                rlf     nrator3,f       ;
                rlf     remain0,f       ;
                rlf     remain1,f       ;
                rlf     remain2,f       ;
                rlf     remain3,f       ;
COPY_RPM_TEST   movf    denom_r3,w      ; subtract denominator from remainder, if no borrow then next bit of quotient is 1
                subwf   remain3,w       ; if borrow next bit of the quotient is 0
                skpz                    ; check, is the result of remain3 - denom3 exactly 0 ?
                goto    COPY_RPM_NOTZ   ; no, go check for negative result
                movf    denom_r2,w      ; yes, continue
                subwf   remain2,w       ; do test subtraction
                skpz                    ; check, is the result of remain2 - denom2 exactly 0 ?
                goto    COPY_RPM_NOTZ   ; no, go check for negative result
                movf    denom_r1,w      ; yes, continue
                subwf   remain1,w       ; do test subtraction
                skpz                    ; check, is the result of remain1 - denom1 exactly 0 ?
                goto    COPY_RPM_NOTZ   ; no,  go check for negative result
                movf    denom_r0,w      ; yes, continue
                subwf   remain0,w       ; do test subtraction
COPY_RPM_NOTZ   skpc                    ; is the result of any remain - denom less than 0 thus negative ?
                goto    COPY_RPM_NOGO   ; yes, skip subtraction, this quotient bit will be zero
COPY_RPM_SUB2   movf    denom_r0,w      ; no, start with real subtraction, the resulting interval is put in denominator
                subwf   remain0,f       ;
                movf    denom_r1,w      ;
                skpc                    ; is remain0 - denom0 < 0 ?
                incfsz  denom_r1,w      ; yes, 'borrow' from more significant byte
                subwf   remain1,f       ; no, borrow has been skipped, do subtraction
                movf    denom_r2,w      ;
                skpc                    ; is remain1 - denom1 < 0 ?
                incfsz  denom_r2,w      ; yes, 'borrow' from more significant byte
                subwf   remain2,f       ; no, borrow has been skipped, do subtraction
                movf    denom_r3,w      ;
                skpc                    ; is remain2 - denom2 < 0 ?
                incfsz  denom_r3,w      ; yes, 'borrow' from more significant byte
                subwf   remain3,f       ; no, borrow has been skipped, do subtraction
                setc                    ; this quotient bit is one
COPY_RPM_NOGO   rlf     rpmmax_0,f      ; shift bit into quotient result
                rlf     rpmmax_1,f      ; shift bit into quotient result
                rlf     rpmmax_2,f      ; shift bit into quotient result
                rlf     rpmmax_3,f      ; shift bit into quotient result
                decfsz  divcounter,f    ;       
                goto    COPY_RPM_LOOP   ; go do next bit
                return                  ; done

;--------------------------------------------------------------------------------------------------------------------------------------

COPY_WHEELC                             ; reads the wheel circumference in millimeters from pic eeprom,
                                        ; multiplies this value by 3318 and stores the result in speed_const0..3
                                        ; please see INT_CCP2 code for details
                                        ; to be used in the calculate speed/ccp2 interrupt routine

                                        ; calculate product (input values are 16 bits, the result is 32 bits):
                                        ; number1    :  -               -               mult_a1         mult_a0
                                        ; number2    :  -               -               mult_b1         mult_b0
                                        ; result P   :  speed_const3    speed_const2    speed_const1    speed_const0

                                        ; for minimum execution time number1 should be the smaller number of the two

MULT16X16       bank0                   ; multiply the two 16-bit numbers 3318 and circumference and store the 32 bit result
                movlw   d'0'            ; eeprom address of low byte of wheel diameter
                pagesel IEE_READ        ; make right program memory page selection
                call    IEE_READ        ; read pic eeprom, address in w, data returned in w, returns in bank2
                movwf   mult_a0         ; store first number in multiplication, low byte
                bank0                   ;
                movlw   d'1'            ; eeprom address of high byte of wheel diameter
                call    IEE_READ        ; read pic eeprom, address in w, data returned in w, returns in bank2
                movwf   mult_a1         ; store first number in multiplication, high byte
                movlw   d'246'          ; 3318
                movwf   mult_b0         ; store second number in multiplication, low byte
                movlw   d'12'           ; 
                movwf   mult_b1         ; store second number in multiplication, high byte
                clrf    speed_const3    ; clear all four bytes of the 32 bit result
                clrf    speed_const2    ;
                clrf    speed_const1    ;
                clrf    speed_const0    ;
                bsf     speed_const1,7  ; set the 16th bit so we can use the speed_const registers as bit counter
                clrc                    ; start off with clear carry
MULTLOOP        rrf     mult_a1,f       ;
                rrf     mult_a0,f       ;
                pagesel MULTSHIFT       ; make right program memory page selection
                skpc                    ; was the least significant bit a one ?
                goto    MULTSHIFT       ; no, bypass addition
                movf    mult_b0,w       ; yes, do addition
                addwf   speed_const2,f  ; add first of two bytes
                movf    mult_b1,w       ;
                skpnc                   ; check for overflow after add and
                incfsz  mult_b1,w       ; increment high byte if necessary 
                addwf   speed_const3,f  ; add second of the two bytes
MULTSHIFT       rrf     speed_const3,f  ; shift to next position
                rrf     speed_const2,f  ;
                rrf     speed_const1,f  ;
                rrf     speed_const0,f  ;
                skpc                    ; are we done with the multiplication ?
                goto    MULTLOOP        ; no, next
                return                  ; done

;--------------------------------------------------------------------------------------------------------------------------------------

COPY_LOGRATES                           ; copy the values from pic eeprom addresses to registers


                movlw   d'16'           ; internal eeprom address
                pagesel IEE_READ        ; make right program memory page selection
                call    IEE_READ        ; read pic eeprom, address in w, data returned in w, returns in bank2
                bank3                   ;
                movwf   freq_rpm        ; store value
                movlw   d'17'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_speed      ;
                movlw   d'18'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_lambda     ;
                movlw   d'19'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_voltage    ;
                movlw   d'20'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_tc         ;
                movlw   d'21'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_air        ;
                movlw   d'22'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_water      ;
                movlw   d'23'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_throttle   ;
                movlw   d'24'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_long       ;
                movlw   d'25'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_lat        ;
                movlw   d'26'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_mark       ;
                movlw   d'27'           ;
                call    IEE_READ        ;
                bank3                   ;
                movwf   freq_brake      ;
                return                  ; done

;--------------------------------------------------------------------------------------------------------------------------------------

COPY_ERRORFLAGS                         ; copy the value of the error flags register from pic eeprom addresses to the register

                movlw   d'15'           ; internal eeprom address
                pagesel IEE_READ        ; make right program memory page selection
                call    IEE_READ        ; read pic eeprom, address in w, data returned in w, returns in bank2
                movwf   errors          ; store value
                return                  ; done

;---------------------------------------------------------------------------------------------------------------------------

FLASHREAD                               ; read from a location in the flash program memory
                                        ; address should be in eeadrh(bank2) and eeadr(bank2)
                                        ; data is returned in eedath(bank2) and eedata(bank2)

                bank3                   ; 
                bsf     eecon1,eepgd    ; point to flash program memory
                bsf     eecon1,rd       ; start read operation
                nop                     ; processor has to wait while data is being read
                nop                     ; wait
                return                  ; return in bank3

;---------------------------------------------------------------------------------------------------------------------------

FLASHWRITE                              ; write to a location in the flash program memory
                                        ; address should be in eeadrh(bank2) and eeadr(bank2)
                                        ; data should be in eedath(bank2) and eedata(bank2)
                                        ; data will be verified, carry set means error, carry cleared means write was ok
                                        ; uses templow and tempmiddle registers during verification

                setc                    ; set error flag, presuming error
                bank2                   ;
                movf    eedata,w        ; make a copy of the dataword value to use after the write
                movwf   templow         ; to check if the write was ok
                movf    eedath,w        ;
                movwf   tempmiddle      ;
                bank3                   ;
                bsf     eecon1,eepgd    ; point to flash program memory
                bsf     eecon1,wren     ; enable writes
                movlw   h'55'           ; sequence needed to unlock pic write safety lock,
                movwf   eecon2          ; used to prevent adverse writes
                movlw   h'AA'           ;
                movwf   eecon2          ;
                bsf     eecon1,wr       ; start write operation
                nop                     ; two nops allow pic to setup for write and then
                nop                     ; the processor will stop execution for the duration of the entire write cycle
                bcf     eecon1,wren     ; disable writes
                call    FLASHREAD       ; read the value of the flash program memory location into eedata and eedath
                bank2                   ;
                movf    eedata,w        ; copy the low byte value to w
                subwf   templow,w       ; compare it with the stored value to see if there was a write error
                skpz                    ; was there a write error in the low byte ?
                return                  ; yes, return with error flag (carry) set, return in bank2
                movf    eedath,w        ; no, now check high byte
                subwf   tempmiddle,w    ; compare it with the stored value to see if there was a write error
                skpz                    ; was there a write error in the high byte ?
                return                  ; yes, return with error flag (carry) set, return in bank2
                clrc                    ; reset error flag
                return                  ; return in bank2


;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------


                org     h'0800'         ; start of program memory page 1

                                        ; Note: The contents of the PCLATH register are unchanged after a RETURN or RETFIE instruction
                                        ; is executed ! The user must rewrite the contents of the PCLATH register for any subsequent
                                        ; subroutine calls or GOTO instructions

;--------------------------------------------------------------------------------------------------------------------------------------


MAIN                                    ; main program code


MAIN_ONLOOP                             ; program continously loops to here while rotary switch is set to 'on',
                pagesel EXEC_COMMAND    ; prepare the right memory page in case we do call
                btfsc   flags1,command  ; commands are received via interrupt routine, has a complete command been put in the buffer ?
                call    EXEC_COMMAND    ; yes, handle it
                bank0                   ; no, see if the position of the rotary switch has changed from 'on' to 'log'
                pagesel MAIN_ONLOOP     ; make right program memory page selection
                btfss   portb,switch    ; is the position of the rotary switch currently 'log' ?
                goto    MAIN_ONLOOP     ; no, go back and see if a command has arrived we should handle
                btfsc   flags1,stx      ; yes, are we receiving a command at the moment ?
                goto    MAIN_ONLOOP     ; yes, finish reception first to minimize communication errors
MAIN_LOGTEST    bank0                   ; we get here if the rotary switch is set to log
                bsf     portc,cts_out   ; clear CTS to hold data stream from computer, we need all processor time for   
                bcf     rcsta,cren      ; the logging, so stop reception and ignore any incoming commands
                bcf     portb,led_red   ; turn off the red led
                bsf     portb,led_green ; turn on the green led
                bank3                   ; we want to make sure there are channels enabled for logging
                movf    freq_rpm,w      ; so check all channels for their setting
                andwf   freq_speed,w    ;
                andwf   freq_lambda,w   ;
                andwf   freq_voltage,w  ;
                andwf   freq_tc,w       ;
                andwf   freq_air,w      ;
                andwf   freq_water,w    ;
                andwf   freq_throttle,w ;
                andwf   freq_long,w     ;
                andwf   freq_lat,w      ;
                andwf   freq_mark,w     ;
                andwf   freq_brake,w    ;
                andlw   b'10000000'     ; bit seven will be set if no channels are enabled
                skpz                    ; are there any channels enabled for logging ? 
                goto    MAIN_FLASHLED   ; no, all channels are turned off, go flash status led
MAIN_LOGSTART
                                        ; **** read errors flag register from eeprom

                call    MEM_OPEN        ; yes, initialize memory, get location for new data, store startdate and -time, bank1 return 
                bcf     flags2,lognow   ; before starting actual logging clear this flag, it will be set during every timer0 interrupt
                bank0                   ;
                clrf    tmr0            ; clear timer0 register
                bcf     intcon,t0if     ; clear the timer 0 overflow flag
                movlw   d'7'            ; location of control byte in clock chip
                bank2                   ; turn square wave of real time clock chip on
                movwf   clockaddr       ; select this location
                movlw   b'10010011'     ; clock control byte, set square wave at 32768 Hz, enabled
                call    IIC_WR_CLKCHIP  ; write control byte to clock chip, bank1 return
                movlw   d'255'          ; we want to start logging and get a value stored for 
                bank3                   ; all channels so use 255 as value since the register value
                movwf   logcounter      ; will be increased to zero (log all channels) the first time
MAIN_LOGLOOP    movlw   d'7'            ; show to the user how much room for storing the data there is left
                bank1                   ; up to ca. 90% (seven out of eight chips) memory filled only green led is on, 
                subwf   mem_chip,w      ; above 90% full the red led is also turned on: red and green together (extra bright)
                bank0                   ; see how many of the eeprom chips we have used so far 
                skpnc                   ; have we started filling the last eeprom chip ?
                bsf     portb,led_red   ; yes, turn on the red led as well
                btfsc   flags2,memfull  ; is all memory completely full ?
                goto    MAIN_LOGSTOP    ; yes, exit logloop to do cleanup and then flash red led to notify user 
                btfsc   intcon,t0if     ; no, did we get a timer0 overflow interrupt so should we log data ?
                call    LOGDATA         ; yes, actual measurement for the different input channels and the data storage is done here
                bank0                   ; no,
                btfsc   portb,switch    ; has the position of the rotary switch been changed back to 'on' ?
                goto    MAIN_LOGLOOP    ; no, keep doing the logging stuff
MAIN_LOGSTOP    bank2                   ; yes, go turn off square wave of real time clock chip to save battery power
                movlw   d'7'            ; location of control byte in clock chip
                movwf   clockaddr       ; point to this location
                movlw   b'10000011'     ; set control value to square wave at 32768 Hz but disabled, output high
                call    IIC_WR_CLKCHIP  ; write control byte to clock chip, bank1 return
                call    MEM_CLOSE       ; write last data block to eeprom, write end position to toc, return in unknown bank
                bank0                   ; we use this logstop routine in two cases, normal stop and memory full, which one was it ?
                btfss   portb,switch    ; has the position of the rotary switch been changed back to 'on' ?
                goto    MAIN_CLEANUP    ; yes, skip memory full
MAIN_MEMFULL    bsf     errors,err_mf   ; set error flag
MAIN_FLASHLED   bank0                   ; we will flash red status led at about 2 Hz to notify user that the memory is full
                bcf     portb,led_green ; turn off the green status led
MAIN_FLASHLOOP  bank2
                btfsc   timer1y,2       ; use timer1y bit for flash rate
                bsf     portb,led_red   ; turn on red status led
                btfss   timer1y,2       ; 
                bcf     portb,led_red   ; turn off red status led
                bank0                   ;
                btfsc   portb,switch    ; has the position of the rotary switch been changed back to 'on' ?
                goto    MAIN_FLASHLOOP  ; no, keep waiting until the user to switches from 'log' back to 'on' status
MAIN_CLEANUP    movlw   d'15'           ; eeprom address for error flags register
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                movf    errors,w        ; get the value of the error flags register
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                bank0                   ; 
                bsf     rcsta,cren      ; re-enable reception for incoming commands
                bcf     portc,cts_out   ; set CTS to re-enable data stream from computer
                bcf     portb,led_green ; turn off the green status led
                bsf     portb,led_red   ; turn on the red status led
                goto    MAIN_ONLOOP     ; done


;--------------------------------------------------------------------------------------------------------------------------------------
; Handle logging flag (set during Timer0 interrupt) log data to external eeprom
;--------------------------------------------------------------------------------------------------------------------------------------

LOGDATA                                 ; timer0 is used as the timebase for the logging event
                                        ; when logging is active this routine is called 32 times every second  
                                        ; acquire input data and move it to the external eeprom
                                        ; check which channels need to be logged

                                        ; lograte table:

                                        ; 00000000       32 Hz
                                        ; 00000001       16 Hz
                                        ; 00000011        8 Hz
                                        ; 00000111        4 Hz
                                        ; 00001111        2 Hz
                                        ; 00011111        1 Hz or every second
                                        ; 00111111      1/2 Hz or every 2 seconds
                                        ; 01111111      1/4 Hz or every 4 seconds
                                        ; 1xxxxxxx      never

LOG_CLEARFLAG   bcf     intcon,t0if     ; clear the flag that brought us here (timer 0 overflow)
                bank3                   ;
                incf    logcounter,f    ; counter is increased every time, so the very first time it will be 0
                bsf     logcounter,7    ; this bit is used to skip channels so we want it always set
LOG_RPM         movf    freq_rpm,w      ;
                andwf   logcounter,w    ; compare counter to the bits of the lograte setting (see above)
                skpz                    ; is the result zero and should we take action ?
                goto    LOG_SPEED       ; no, see if we should log the next channel 
                call    CALC_RPM        ; yes, calculate the RPM value from the time interval between the input pulses, return in bank2
                movf    rpm_low,w       ; get low byte value (two bytes altogether for 0..16383 RPM) 
                movwf   numlow          ;
                movf    rpm_high,w      ; get high byte value
                movwf   nummiddle       ;
                call    MEM_ADD14       ; write fourteen bits to the external eeprom
LOG_SPEED       bank3                   ;
                movf    freq_speed,w    ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_LAMBDA      ;
                call    CALC_SPEED      ; calculate speed value from time interval between input pulses, return in bank2
                movf    speed,w         ; get value, maximum speed is limited to 255 km/hr (see ccp2 interrupt)
                movwf   numlow          ; copy value 
                call    MEM_ADD8        ;
LOG_LAMBDA      bank3                   ;
                movf    freq_lambda,w   ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_VOLTAGE     ;
                movlw   d'0'            ; lambda is analog channel 0
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                call    MEM_ADD10       ;
LOG_VOLTAGE     bank3                   ;
                movf    freq_voltage,w  ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_TC          ;
                movlw   d'1'            ; 0..5 V voltage input is analog channel 1
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                call    MEM_ADD10       ;
LOG_TC          bank3                   ;
                movf    freq_tc,w       ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_AIRTEMP     ;
                movlw   d'2'            ; thermocouple is analog channel 2
                bank0                   ;
                btfss   portb,not_tcd   ; is the thermocouple disconnected ?
                bsf     errors,tcdiscon ; yes, set error flag
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                call    MEM_ADD10       ;
LOG_AIRTEMP     bank3                   ;
                movf    freq_air,w      ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_WATERTEMP   ;
                movlw   d'3'            ; air temperature is analog channel 3
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                call    MEM_ADD10       ;
LOG_WATERTEMP   bank3                   ;
                movf    freq_water,w    ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_THROTTLE    ;
                movlw   d'4'            ; water temperature is analog channel 4
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                call    MEM_ADD10       ;
LOG_THROTTLE    bank3                   ;
                movf    freq_throttle,w ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_LONG        ;
                movlw   d'5'            ; throttle is analog channel 5
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                call    MEM_ADD10       ;
LOG_LONG        bank3                   ;
                movf    freq_long,w     ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_LAT         ;
                movlw   d'6'            ; longitudinal acceleration is analog channel 6
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                call    MEM_ADD10       ;
LOG_LAT         bank3                   ;
                movf    freq_lat,w      ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_MARK        ;
                movlw   d'7'            ; lateral accelation is analog channel 7
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                call    MEM_ADD10       ;
LOG_MARK        bank3                   ;
                movf    freq_mark,w     ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_BRAKE       ;
                clrw                    ; start with clearing w register
                bank0                   ;
                btfss   portb,not_mark  ; test if mark input is high, use inverse value for not_mark signal
                movlw   b'00000001'     ; change if the value should be 1
                call    MEM_ADD1        ;
LOG_BRAKE       bank3                   ;
                movf    freq_brake,w    ;
                andwf   logcounter,w    ;
                skpz                    ;
                goto    LOG_CHK_OVERFL  ;
                clrw                    ; start with clearing w register
                bank0                   ;
                btfsc   portb,brake     ; test if brake input is high   
                movlw   b'00000001'     ; change if the value should be 1
                call    MEM_ADD1        ;
LOG_CHK_OVERFL  btfsc   intcon,t0if     ; check for log event overflow, we just handled a log event, did we get a new request ?
                bsf     errors,logoflow ; yes, set flag 
LOG_DONE        return                  ; no, done



;--------------------------------------------------------------------------------------------------------------------------------------
; Calculate the RPM rate
;--------------------------------------------------------------------------------------------------------------------------------------

CALC_RPM
                                        ; calculate RPM (input values are 32 bits, the result is 16 bits):
                                        ; numerator  :  nrator3         nrator2         nrator1         nrator0
                                        ; denominator:  denom_r3        denom_r2        denom_r1        denom_r0
                                        ; quotient   :  -               -               rpm_high        rpm_low
                                        ; remainder  :  remain3         remain2         remain1         remain0

                                        ; only the lower 16 bits of the 32 bit quotient are used as rpm result
                                        ; the remainder R is not used
                                        ; after that even more bits are thrown away, a maximum rpm rate of 16383 rpm is
                                        ; more than sufficient and only needs 14 bits

                                        ; see also INT_CCP1 routine

CALC_RPM_COPY   bcf     intcon,gie      ; disable interrupts during copy
                bank2                   ;
                movf    ccp1interval3,w ; copy rpm interval for later use in division routine
                movwf   denom_r3        ;
                movf    ccp1interval2,w ;
                movwf   denom_r2        ;
                movf    ccp1interval1,w ;
                movwf   denom_r1        ;
                movf    ccp1interval0,w ;
                movwf   denom_r0        ; these values are used to calculate the rpm rate
                bsf     intcon,gie      ; re-enable interrupts

CALC_RPM_DIV    movlw   d'3'            ; set numerator X = 55,296,000 = 3 : 75 : 192 : 0
                movwf   nrator3         ;
                movlw   d'75'           ;
                movwf   nrator2         ;
                movlw   d'192'          ;
                movwf   nrator1         ;
                movlw   d'0'            ;
                movwf   nrator0         ;
                movlw   d'32'           ; there are 32 bits in this division
                movwf   divcounter      ;
                clrf    rpm_low         ; clear rpm result so we can check for 2 byte overflow (rpm > 65535)
                clrf    rpm_high        ;
                clrf    remain0         ; clear remainder
                clrf    remain1         ;
                clrf    remain2         ;
                clrf    remain3         ;
CALC_RPM_LOOP   clrc                    ;
                rlf     nrator0,f       ; shift next bit to remainder (msbit numerator to lsbit remainder)
                rlf     nrator1,f       ;
                rlf     nrator2,f       ;
                rlf     nrator3,f       ;
                rlf     remain0,f       ;
                rlf     remain1,f       ;
                rlf     remain2,f       ;
                rlf     remain3,f       ;
CALC_RPM_TEST   movf    denom_r3,w      ; subtract denominator from remainder, if no borrow then next bit of quotient is 1
                subwf   remain3,w       ; if borrow next bit of the quotient is 0
                skpz                    ; check, is the result of remain3 - denom3 exactly 0 ?
                goto    CALC_RPM_NOTZ   ; no, go check for negative result
                movf    denom_r2,w      ; yes, continue
                subwf   remain2,w       ; do test subtraction
                skpz                    ; check, is the result of remain2 - denom2 exactly 0 ?
                goto    CALC_RPM_NOTZ   ; no, go check for negative result
                movf    denom_r1,w      ; yes, continue
                subwf   remain1,w       ; do test subtraction
                skpz                    ; check, is the result of remain1 - denom1 exactly 0 ?
                goto    CALC_RPM_NOTZ   ; no,  go check for negative result
                movf    denom_r0,w      ; yes, continue
                subwf   remain0,w       ; do test subtraction
CALC_RPM_NOTZ   skpc                    ; is the result of any remain - denom less than 0 thus negative ?
                goto    CALC_RPM_NOGO   ; yes, skip subtraction, this quotient bit will be zero
CALC_RPM_SUB2   movf    denom_r0,w      ; no, start with real subtraction
                subwf   remain0,f       ;
                movf    denom_r1,w      ;
                skpc                    ; is remain0 - denom0 < 0 ?
                incfsz  denom_r1,w      ; yes, 'borrow' from more significant byte
                subwf   remain1,f       ; no, borrow has been skipped, do subtraction
                movf    denom_r2,w      ;
                skpc                    ; is remain1 - denom1 < 0 ?
                incfsz  denom_r2,w      ; yes, 'borrow' from more significant byte
                subwf   remain2,f       ; no, borrow has been skipped, do subtraction
                movf    denom_r3,w      ;
                skpc                    ; is remain2 - denom2 < 0 ?
                incfsz  denom_r3,w      ; yes, 'borrow' from more significant byte
                subwf   remain3,f       ; no, borrow has been skipped, do subtraction
                setc                    ; this quotient bit is one
CALC_RPM_NOGO   rlf     rpm_low,f       ; shift bit into quotient result, store lower 16 bits of quotient as result  
                rlf     rpm_high,f      ; throw away the upper 16 bits
                skpnc                   ; do we have a rpm rate of more than 65535 rpm ?
                goto    CALC_RPM_OFLOW  ; yes, the result will not fit in the 16 bits of rpm_low and rpm_high, exit
                decfsz  divcounter,f    ;       
                goto    CALC_RPM_LOOP   ; go do next bit
CALC_RPM_2PULS  btfsc   pulses,0        ; do we have only one puls per revolution of the crankshaft ?
                goto    CALC_RPM_14BIT  ; yes, we do not need to adjust the value
                rrf     rpm_high,f      ; no, so we have two pulses per revolution, we have to divide the value by two
                rrf     rpm_low,f       ; no need to clear carry, it was already 'cleared' by previous instructions  
CALC_RPM_14BIT  movlw   b'11000000'     ; test value to see if we use the upper 2 bits of the 16 bit rpm value 
                andwf   rpm_high,w      ; do the test
                skpz                    ; is the rpm value more than 16383 (the maximum value that will fit in 14 bits) ?
                goto    CALC_RPM_OFLOW  ; yes, go set to maximum value (16383)
                return                  ; no, we're done here, return in bank2


CALC_RPM_OFLOW  movlw   d'255'          ; we have an overflow, set to 14 bit maximum (16383) instead
                movwf   rpm_low         ;
                movlw   d'63'           ;
                movwf   rpm_high        ;
                return                  ; return in bank2


;--------------------------------------------------------------------------------------------------------------------------------------
; Calculate the Speed value
;--------------------------------------------------------------------------------------------------------------------------------------


CALC_SPEED                              ; since we can have both timer 1 overflow and ccp interrupt request at the same
                                        ; time it is important to update the timer 1 extension bytes at the right time,
                                        ; now see if we can use the present timer 1 values to calculate the vehicle speed
                                        ; from CCP module 2 using the time (t) between two captures, otherwise we exit this
                                        ; routine and come back later when timer 1 has been incremented

                                        ; we assume there is one puls per revolution of the wheel
                                        ; t [s]         = time between pulses = 1 / rotation frequency of wheel = 1 / F [Hz]
                                        ; c [mm]        = circumference wheel = distance per revolution of the wheel
                                        ; SPEED [mm/s]  =  F * c
                                        ; SPEED [km/hr] = (F * c * 3600)    / 1000000
                                        ; SPEED [km/hr] = ((1 / t) * c * 3600) / 1000000
                                        ; SPEED [km/hr] = (c * 3600) / (t * 1000000)
                                        ; SPEED [km/hr] = (c * 3600) / ((1 / (timer1clockrate /  interval)) * 1000000)
                                        ; SPEED [km/hr] = (c * 3600) / ((1 / (3686400 Hz / interval)) * 1000000)
                                        ; SPEED [km/hr] = (921600 * c * 3600) / (interval * 1000000)
                                        ; SPEED [km/hr] = (3318 * c) / interval
                                        ; SPEED [km/hr] = (3318 * c) / (capturevalue - previouscapturevalue)

                                        ; the value 3318 * c has been calculated during intialization at processor reset
                                        ; and is stored in speed_const0..3        (value 3318 is actually 3317.76)

                                        ; calculate interval (32 bits):
                                        ; current  =    timer1z         timer1y         ccpr1h          ccpr1l
                                        ; new      =    new2cap3        new2cap2        new2cap1        new2cap0
                                        ; old      =    old2cap3        old2cap2        old2cap1        old2cap0
                                        ; interval =    denom_s3        denom_s2        denom_s1        denom2_0

                                        ; calculate speed (input values are 32 bits, the result is 16 bits):
                                        ; quotient Q    =    numerator X       /  denominator Y  (and remainder R)
                                        ; numerator  :  nrator3         nrator2         nrator1         nrator0
                                        ; denominator:  denom_s3        denom_s2        denom_s1        denom2_0
                                        ; result Q   :  -               -               -               speed
                                        ; remainder  :  remain3         remain2         remain1         remain0

                                        ; only the lower 8 bits of the 32 bit quotient are used as speed result,
                                        ; the other bits are thrown away since a top speed of 255 km/hr is more than
                                        ; sufficient and can be stored as just one byte

                                        ; final speed value (0..255 km/hr) is stored in speed register in bank2

CALC_SPEED_COPY bcf     intcon,gie      ; disable interrupts during copy
                bank2                   ; 
                movf    ccp2interval3,w ; copy speed interval for use in division routine
                movwf   denom_s3        ;
                movf    ccp2interval2,w ;
                movwf   denom_s2        ;
                movf    ccp2interval1,w ;
                movwf   denom_s1        ;
                movf    ccp2interval0,w ;
                movwf   denom_s0        ; use these values to calculate vehicle speed
                bsf     intcon,gie      ; re-enable interrupts

CALC_SPEED_DIV  movf    speed_const3,w  ; set numerator X = 3318 * circumference
                movwf   nrator3         ;
                movf    speed_const2,w  ;
                movwf   nrator2         ;
                movf    speed_const1,w  ;
                movwf   nrator1         ;
                movf    speed_const0,w  ;
                movwf   nrator0         ;
                movlw   d'32'           ; there are 32 bits in this division
                movwf   divcounter      ;
                clrf    speed           ; clear speed result so we can check for 1 byte overflow (speed > 255)
                clrf    remain0         ; clear remainder
                clrf    remain1         ;
                clrf    remain2         ;
                clrf    remain3         ;
CALC_SPEED_LOOP clrc                    ;
                rlf     nrator0,f       ; shift next bit to remainder (msbit numerator to lsbit remainder)
                rlf     nrator1,f       ;
                rlf     nrator2,f       ;
                rlf     nrator3,f       ;
                rlf     remain0,f       ;
                rlf     remain1,f       ;
                rlf     remain2,f       ;
                rlf     remain3,f       ;
CALC_SPEED_TEST movf    denom_s3,w      ; subtract denominator from remainder, if no borrow then next bit of quotient is 1
                subwf   remain3,w       ; if borrow next bit of the quotient is 0
                skpz                    ; check, is the result of remain3 - denom3 exactly 0 ?
                goto    CALC_SPEED_NOTZ ; no, go check for negative result
                movf    denom_s2,w      ; yes, continue
                subwf   remain2,w       ; do test subtraction
                skpz                    ; check, is the result of remain2 - denom2 exactly 0 ?
                goto    CALC_SPEED_NOTZ ; no, go check for negative result
                movf    denom_s1,w      ; yes, continue
                subwf   remain1,w       ; do test subtraction
                skpz                    ; check, is the result of remain1 - denom1 exactly 0 ?
                goto    CALC_SPEED_NOTZ ; no,  go check for negative result
                movf    denom_s0,w      ; yes, continue
                subwf   remain0,w       ; do test subtraction
CALC_SPEED_NOTZ skpc                    ; is the result of any remain - denom less than 0 thus negative ?
                goto    CALC_SPEED_NOGO ; yes, skip subtraction, this quotient bit will be zero
CALC_SPEED_RSUB movf    denom_s0,w      ; no, start with real subtraction
                subwf   remain0,f       ;
                movf    denom_s1,w      ;
                skpc                    ; is remain0 - denom0 < 0 ?
                incfsz  denom_s1,w      ; yes, 'borrow' from more significant byte
                subwf   remain1,f       ; no, borrow has been skipped, do subtraction
                movf    denom_s2,w      ;
                skpc                    ; is remain1 - denom1 < 0 ?
                incfsz  denom_s2,w      ; yes, 'borrow' from more significant byte
                subwf   remain2,f       ; no, borrow has been skipped, do subtraction
                movf    denom_s3,w      ;
                skpc                    ; is remain2 - denom2 < 0 ?
                incfsz  denom_s3,w      ; yes, 'borrow' from more significant byte
                subwf   remain3,f       ; no, borrow has been skipped, do subtraction
                setc                    ; this quotient bit is one
CALC_SPEED_NOGO rlf     speed,f         ; shift bit into quotient result, store lower 8 bits of quotient as result  
                skpnc                   ; do we have a speed of more than 255 km/hr ?
                goto    CALC_SPEED_OFLW ; yes, the result will not fit in the 8 bits of speed register, exit
                decfsz  divcounter,f    ;       
                goto    CALC_SPEED_LOOP ; go do next bit
                return                  ; we're done here, return in bank2


CALC_SPEED_OFLW movlw   d'255'          ; we have an overflow, set to 8 bit maximum (255) instead
                movwf   speed           ;
                return                  ; done, return in bank2


;--------------------------------------------------------------------------------------------------------------------------------------
; analog input subroutine
;--------------------------------------------------------------------------------------------------------------------------------------

GET_ANALOG                              ; get 10 bit value of one analog input
                                        ; 3 bit channel number should be in w register (0..7)
                                        ; w register content is destroyed
                                        ; value is returned in numlow and nummiddle

                movwf   adtemp          ; use adtemp register in channel selection calculation
                swapf   adtemp,f        ; move channel selection bits to the right position
                rrf     adtemp,w        ; move channel selection bits to the right position, no need to clear carry:
                andlw   b'00111000'     ; mask out all but the three bits that hold channel number
                iorlw   b'01000000'     ; Fosc/8 gives 2.2 us bit conversion time@Fosc=3.6864 MHz), channel 0, a/d module off
                bank0                   ;
                movwf   adcon0          ; apply settings
                bsf     adcon0,adon     ; activate a/d module
                movlw   d'7'            ; wait the required acquisition time (approx. 20 microseconds), which is
                movwf   adtemp          ; about 19 instruction cycles @ 3.6864 Mhz (1085 ns per instruction)
ADLOOP1         decfsz  adtemp,f        ; count down from to zero, this instruction is one cycle
                goto    ADLOOP1         ; this instruction is two cycles, wait until we are done (7*3*1.085=22.8 us)
                bsf     adcon0,go_notdone ; now start conversion
ADLOOP2         btfsc   adcon0,go_notdone ; is conversion done ?
                goto    ADLOOP2         ; no, wait here until conversion is completed 
                bcf     adcon0,0        ; shut-off a/d module for minimal power consumption
                movf    adresh,w        ; store results, note that this will also give us the neccesary delay of 2 Tad
                movwf   nummiddle       ; between two a/d sampling actions (1.085 us per instruction @ 3.6864 Mhz )
                bank1                   ;
                movf    adresl,w        ; 
                movwf   numlow          ;
                return                  ; return in bank1

;--------------------------------------------------------------------------------------------------------------------------------------
; PIC internal EEPROM read and write routines
;--------------------------------------------------------------------------------------------------------------------------------------

IEE_READ                                ; reads one byte from PIC EEPROM
                                        ; address should be in w
                                        ; data byte is returned in w
                                        ; returns in bank 2

                bank2                   ;
                movwf   eeadr           ; register address (0-255 decimal for PIC16F877)
                bank3                   ;
                bcf     eecon1,eepgd    ; select eeprom data memory for read/write access instead of program memory
                bsf     eecon1,rd       ; set bit to read
                bank2                   ;
                movf    eedata,w        ;
                return                  ;

;--------------------------------------------------------------------------------------------------------------------------------------

IEE_WRITE                               ; writes one byte to PIC EEPROM
                                        ; address should be in iee_address (bank2), data should be in w
                                        ; w content is destroyed
                                        ; returns in bank 0

                bank3                   ;
                btfsc   eecon1,wr       ; is there currently a write cycle busy ?
                goto    $-1             ; wait here for previous write to finish
                bank2                   ;
                movwf   eedata          ; set data
                movf    iee_address,w   ; get address
                movwf   eeadr           ; select address
                bank3                   ;
                bcf     eecon1,eepgd    ; select eeprom data memory for read/write access instead of program memory
                bsf     eecon1,wren     ; write enable
                bcf     intcon,gie      ; disable all interrupts
                btfsc   intcon,gie      ; check if disabled
                goto    $-2             ; else try again
                movlw   h'55'           ; required sequence
                movwf   eecon2          ;
                movlw   h'AA'           ;
                movwf   eecon2          ;
                bsf     eecon1,wr       ; start write
                bsf     intcon,gie      ; re-enable interrupts
                bcf     eecon1,wren     ; disable writes (does not affect current write cycle)
                bank0                   ;
                btfss   pir2,eeif       ;
                goto    $-1             ; wait here for the write to complete
                bcf     pir2,eeif       ; clear eeprom write interrupt flag
                return                  ; return in bank0

;--------------------------------------------------------------------------------------------------------------------------------------
; IIC subroutines
;--------------------------------------------------------------------------------------------------------------------------------------

IIC_IDLE        bank1                   ; use this subroutine for interrupt driven IIC communication  
                btfsc   sspstat,r_notw  ; is the IIC bus free (test if a transmission is in progress) ?
                goto    $-1             ; no, wait until it is free
                movf    sspcon2,w       ; yes, get a copy of sspcon2 to test status bits
                andlw   b'00011111'     ; apply mask to mask out non-status bits
                skpz                    ; test for zero state, if zero bus is idle, bus busy ?
                goto    $-3             ; yes, test again until bus is free
                return                  ; no, bus is free, exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_START       bank1                   ;
                bsf     sspcon2,sen     ; generate start condition
                btfsc   sspcon2,sen     ; is the start completed ?
                goto    $-1             ; no, wait until it is completed
                return                  ; yes, exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_RESTART     bank1                   ;
                bsf     sspcon2,rsen    ; generate restart condition
                btfsc   sspcon2,rsen    ; is the restart completed ?
                goto    $-1             ; no, wait until it is completed
                return                  ; yes, exit

;--------------------------------------------------------------------------------------------------------------------------------------
                
IIC_STOP        bank1                   ;
                bsf     sspcon2,pen     ; generate stop condition
                btfsc   sspcon2,pen     ; is the stop completed ?
                goto    $-1             ; no, wait until it is completed
                return                  ; yes, exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_EADDRESS    bank1                   ; send the control byte, high and low address bytes to eeprom
                rlf     iicchip,w       ; get chip select number in right position, no need to clear carry prior to byte rotation
                andlw   b'00001110'     ; avoid possible errors and make sure we only use the three bit number
                iorlw   b'10100000'     ; control code for eeprom chips, chip number 0, bit 0 clear for write operation
                bank0                   ;
                movwf   sspbuf          ; send the slave address
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                return                  ; no, exit and deal with error
                movf    iicahigh,w      ; yes, get upper byte of address (**** to do: current eeproms are only 32kB>15 bit number!)
                bank0                   ;
                movwf   sspbuf          ; send address upper byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                return                  ; no, exit and deal with error
                movf    iicalow,w       ; yes, get lower address byte
                bank0                   ;
                movwf   sspbuf          ; send lower address byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                return                  ; yes, exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_WRBYTE                              ; write one byte to external IIC eeprom
                                        ; address in iicchip, iicalow and iicahigh
                                        ; data in iicdata

                call    IIC_START       ;
                call    IIC_EADDRESS    ; send the address and check for any ACK errors
                btfsc   sspcon2,ackstat ; did we get any ACK error while sending the address ?
                goto    IIC_STOP        ; yes, error, send stop then exit
                movf    iicdata,w       ; no, get data byte
                bank0                   ;
                movwf   sspbuf          ; send data byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
IIC_POLL1       call    IIC_STOP        ; start write cycle, then poll eeprom for completion cycle, wait for ACK from eeprom
                call    IIC_START       ; (on completion of the write cycle the eeprom sends an ACK)
                bank1                   ; send the control byte to eeprom:
                rlf     iicchip,w       ; get chip select number, no need to clear carry prior to byte rotation
                andlw   b'00001110'     ; avoid possible errors and make sure we only use the three bit number
                iorlw   b'10100000'     ; control code for eeprom chips, chip number 0, bit 0 clear for write operation
                bank0                   ;
                movwf   sspbuf          ; send slave address
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_POLL1       ; no, go poll again
                goto    IIC_STOP        ; yes, send stop then exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_RDBYTE                              ; read one byte from external IIC eeprom
                                        ; address in iicchip, iicalow and iicahigh
                                        ; data is returned in iicdata

                call    IIC_START       ;
                call    IIC_EADDRESS    ; send the address and check for any ACK errors
                btfsc   sspcon2,ackstat ; did we get any ACK error while sending the address ?
                goto    IIC_STOP        ; yes, send stop then exit
                call    IIC_RESTART     ; no, send restart
                bank1                   ; send the control byte to eeprom:
                rlf     iicchip,w       ; get chip select number, no need to clear carry prior to byte rotation
                andlw   b'00001110'     ; avoid possible errors and make sure we only use the three bit number
                iorlw   b'10100001'     ; control code for eeprom chips, chip number 0, bit 0 set for read operation
                bank0                   ;
                movwf   sspbuf          ; send slave address
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_STOP        ; no, send stop then exit
                bsf     sspcon2,rcen    ; receive data byte from eeprom
                btfsc   sspcon2,rcen    ; have we received the byte ?
                goto    $-1             ; no, wait here until byte receive is completed
                bank0                   ;
                movf    sspbuf,w        ; get received data byte
                bank1                   ;
                movwf   iicdata         ; store the data byte
                bsf     sspcon2,ackdt   ; select NOACK
                bsf     sspcon2,acken   ; send NOACK
                btfsc   sspcon2,acken   ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                goto    IIC_STOP        ; yes, send stop then exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_WRBLOCK                             ; writes one block (64 bytes) to the external IIC eeprom memory
                                        ; first address in iicchip, iicalow and iicahigh, when address value is not 
                                        ; the start of a block, the address counter will roll over
                                        ; w register content is destroyed
                                        ; data is in block buffer in bank1, from address AF hex up to and including EE hex

                call    IIC_START       ; send start condition
                call    IIC_EADDRESS    ; send the address and check for any ACK errors
                bank1                   ;
                btfsc   sspcon2,ackstat ; did we get any ACK error while sending the address ?
                goto    IIC_STOP        ; yes, error, send stop then exit
                bcf     status,irp      ; no, make sure upper bit in bank address is zero (select bank 0 and 1)
                movlw   blockbuff00     ; start position of data in buffer
                movwf   fsr             ; point to this address
IIC_WRITELOOP   movf    indf,w          ; read data byte from buffer
                bank0                   ;
                movwf   sspbuf          ; send data byte to eeprom
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; has ACK been received ?
                goto    IIC_STOP        ; no, send stop then exit
                incf    fsr,f           ; increase pointer to point to next value
                movf    fsr,w           ; use pointer value
                sublw   blockbuff63     ; was this the last byte ?
                skpnc                   ; let's see
                goto    IIC_WRITELOOP   ; no, do loop again and send next data byte
IIC_POLL2       call    IIC_STOP        ; yes, start block write, poll eeprom for completion cycle, wait for ACK from eeprom
                call    IIC_START       ; (on completion of the write cycle the eeprom sends an ACK)
                bank1                   ; send the control byte to eeprom:
                rlf     iicchip,w       ; get chip select number in right position, no need to clear carry prior to byte rotation
                andlw   b'00001110'     ; avoid possible errors and make sure we only use the three bit number
                iorlw   b'10100000'     ; control code for eeprom chips, chip number 0, bit 0 clear for write operation
                bank0                   ;
                movwf   sspbuf          ; send slave address
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_POLL2       ; no, go poll again
                goto    IIC_STOP        ; yes, send stop then exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_RDBLOCK                             ; reads one block (64 bytes) from the external IIC eeprom memory
                                        ; first address in iicchip, iicalow and iicahigh
                                        ; w register content is destroyed
                                        ; data is returned in buffer in bank1, from address 2F hex up to and including 6E hex
                                        ; return in bank1

                movlw   blockbuff00     ; no, start position of data in buffer
                movwf   fsr             ; point to this address
                bcf     status,irp      ; make sure upper bit in address is zero (select bank 0 and 1)
                call    IIC_START       ; read one byte from external IIC eeprom into iicdata, address in iicchip, iicalow and iicahigh
                call    IIC_EADDRESS    ; send the address and check for any ACK errors
                btfsc   sspcon2,ackstat ; did we get any ACK error while sending the address ?
                goto    IIC_STOP        ; yes, send stop then exit
                call    IIC_RESTART     ; no, send restart
                bank1                   ; send the control byte to eeprom:
                rlf     iicchip,w       ; get chip select number, no need to clear carry prior to byte rotation
                andlw   b'00001110'     ; avoid possible errors and make sure we only use the three bit number
                iorlw   b'10100001'     ; control code for eeprom chips, chip number 0, bit 0 set for read operation
                bank0                   ;
                movwf   sspbuf          ; send slave address
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_STOP        ; no, send stop then exit
IIC_READLOOP    bsf     sspcon2,rcen    ; receive data byte from eeprom
                btfsc   sspcon2,rcen    ; have we received the byte ?
                goto    $-1             ; no, wait here until byte receive is completed
                bank0                   ;
                movf    sspbuf,w        ; get received data byte
                bank1                   ; the buffer is in bank 1
                movwf   indf            ; store the data byte at the right position in the buffer
                incf    fsr,f           ; increase pointer to point to next value
                movf    fsr,w           ; use pointer value
                sublw   blockbuff63     ; was this the last byte ?
                skpc                    ; let's see
                goto    IIC_READDONE    ; yes, send termination NOACK then exit
                bcf     sspcon2,ackdt   ; select ACK
                bsf     sspcon2,acken   ; send ACK
                btfsc   sspcon2,acken   ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                goto    IIC_READLOOP    ; do loop again and read next data byte

IIC_READDONE    bsf     sspcon2,ackdt   ; select NOACK
                bsf     sspcon2,acken   ; send NOACK
                btfsc   sspcon2,acken   ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                goto    IIC_STOP        ; yes, send stop then exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_RDCLOCK                             ; read the eight time related bytes from the external IIC clock chip DS1307
                                        ; into the buffer in bank2, from address 68 hex up to and including 6F hex
                                        ; w register content is destroyed

                call    IIC_START       ;
                movlw   b'11010000'     ; clock control code/slave address, bit 0 is cleared as indication for write
                bank0                   ; 
                movwf   sspbuf          ; send the control byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_STOP        ; no, deal with error: send stop then exit
                bank0                   ;
                clrw                    ; address byte is zero indicating start of clock memory
                movwf   sspbuf          ; send address byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; has ACK been received ?
                goto    IIC_STOP        ; no, deal with error: send stop then exit
                call    IIC_STOP        ; yes, continue
                call    IIC_START       ; send start
                bank0                   ;
                movlw   b'11010001'     ; clock control code/slave address, bit 1 is set as indication for read
                movwf   sspbuf          ; send control byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_STOP        ; no, send stop then exit
                movlw   h'68'           ; no, start position of data in buffer
                movwf   fsr             ; point to this address
                bsf     status,irp      ; make sure upper bit in address is one (select bank 2 and 3)
IIC_CRDLOOP     bank1                   ;
                bsf     sspcon2,rcen    ; receive data byte from clock chip
                btfsc   sspcon2,rcen    ; have we received the byte ?
                goto    $-1             ; no, wait here until byte receive is completed
                bank0                   ;
                movf    sspbuf,w        ; get received data byte
                movwf   indf            ; store the data byte at the right position in the buffer
                incf    fsr,f           ; increase pointer to point to next value
                movf    fsr,w           ; use pointer value
                sublw   h'6F'           ; was this the last byte ?
                skpc                    ; let's see
                goto    IIC_CRDDONE     ; yes, send termination NOACK then exit
                bank1                   ;
                bcf     sspcon2,ackdt   ; select ACK
                bsf     sspcon2,acken   ; send ACK
                btfsc   sspcon2,acken   ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                goto    IIC_CRDLOOP     ; do loop again and read next data byte

IIC_CRDDONE     bank1                   ;
                bsf     sspcon2,ackdt   ; select NOACK
                bsf     sspcon2,acken   ; send NOACK
                btfsc   sspcon2,acken   ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                goto    IIC_STOP        ; yes, send stop then exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_WRCLOCK                             ; write eight time related bytes to the external IIC clock chip DS1307
                                        ; uses the data from buffer in bank2, from 68 hex up to and including 6F hex

                call    IIC_START       ;
                movlw   b'11010000'     ; clock chip control code, bit 0 is cleared as indication for write
                bank0                   ; 
                movwf   sspbuf          ; send the control byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it has completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_STOP        ; no, deal with error: send stop then exit
                bank0                   ; yes, 
                clrw                    ; address byte is zero indicating start of clock memory
                movwf   sspbuf          ; send address byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it has completed
                btfsc   sspcon2,ackstat ; has ACK been received ?
                goto    IIC_STOP        ; no, deal with error: send stop then exit
                movlw   h'68'           ; yes, start position of data in buffer
                movwf   fsr             ; point to this address
                bsf     status,irp      ; make sure upper bit in address is one (select bank 2 and 3)
IIC_CWRLOOP     bank0                   ;
                movf    indf,w          ; read data byte from buffer
                movwf   sspbuf          ; send data byte to clock
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it has completed
                btfsc   sspcon2,ackstat ; has ACK been received ?
                goto    IIC_STOP        ; no, send stop then exit
                incf    fsr,f           ; increase pointer to point to next value
                movf    fsr,w           ; use pointer value
                sublw   h'6F'           ; was this the last byte ?
                skpnc                   ; let's see
                goto    IIC_CWRLOOP     ; no, do loop again and send next data byte
                goto    IIC_STOP        ; yes, send stop then exit

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_RD_CLKCHIP                          ; read a byte from the control bytes or static ram of the clock chip DS1307
                                        ; address in w register (0..63 addresses include the time settings and wrap around !)
                                        ; returns with value in w register

                bank2                   ;
                movwf   clockaddr       ; store the address byte for later use
                call    IIC_START       ;
                movlw   b'11010000'     ; clock control code/slave address, bit 0 is cleared as indication for write
                bank0                   ; 
                movwf   sspbuf          ; send the control byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_STOP        ; no, deal with error: send stop then exit
                bank2                   ;
                movf    clockaddr,w     ; get the address byte
                bank0                   ;
                movwf   sspbuf          ; send address byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; has ACK been received ?
                goto    IIC_STOP        ; no, deal with error: send stop then exit
                call    IIC_STOP        ; yes, continue
                call    IIC_START       ; send start
                bank0                   ;
                movlw   b'11010001'     ; clock control code/slave address, bit 1 is set as indication for read
                movwf   sspbuf          ; send control byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_STOP        ; no, send stop then exit
IIC_RSTLOOP     bank1                   ;
                bsf     sspcon2,rcen    ; receive data byte from clock chip
                btfsc   sspcon2,rcen    ; have we received the byte ?
                goto    $-1             ; no, wait here until byte receive is completed
                bank0                   ;
                movf    sspbuf,w        ; get received data byte
                bank2                   ;
                movwf   clockdata       ; store data byte for later use
                bank1                   ;
                bsf     sspcon2,ackdt   ; select NOACK
                bsf     sspcon2,acken   ; send termination NOACK
                btfsc   sspcon2,acken   ; is the transmission completed ?
                goto    $-1             ; no, wait here until it is completed
                call    IIC_STOP        ; yes, send stop
                bank2                   ;
                movf    clockdata,w     ; retrieve data value
                return                  ; return in bank2

;--------------------------------------------------------------------------------------------------------------------------------------

IIC_WR_CLKCHIP                          ; write one byte to the control bytes or static ram of the clock chip DS1307
                                        ; address in clockaddr register (0..63 addresses include the time settings and wrap around !)
                                        ; data in w register
                                        ; w register content is destroyed
                
                bank2                   ;
                movwf   clockdata       ; store data byte for later use
                call    IIC_START       ;
                movlw   b'11010000'     ; clock chip control code, bit 0 is cleared as indication for write
                bank0                   ; 
                movwf   sspbuf          ; send the control byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it has completed
                btfsc   sspcon2,ackstat ; yes, has ACK been received ?
                goto    IIC_STOP        ; no, deal with error: send stop then exit
                bank2                   ; yes,
                movf    clockaddr,w     ; get address byte
                bank0                   ;  
                movwf   sspbuf          ; send address byte
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it has completed
                btfsc   sspcon2,ackstat ; has ACK been received ?
                goto    IIC_STOP        ; no, deal with error: send stop then exit
                bank2                   ; yes,
                movf    clockdata,w     ; get data byte
                bank0                   ;
                movwf   sspbuf          ; send data byte to clock
                bank1                   ;
                btfsc   sspstat,r_notw  ; is the transmission completed ?
                goto    $-1             ; no, wait here until it has completed
                goto    IIC_STOP        ; send stop then exit


;--------------------------------------------------------------------------------------------------------------------------------------


MEMORY                                  ; EEPROMS - measured data is stored in the EXTERNAL eeprom, the location of this data in the
                                        ; external eeprom and starttime information are stored in the INTERNAL eeprom, meaning that
                                        ; the internal eeprom serves as a table of contents (TOC)

                                        ; TOC - the table of contents runs from '@tocstart' up to and including address 255,
                                        ; at the start of the logging the start time and date is stored in the toc, when closing the
                                        ; record file (after switching back from the 'log' to the 'on' mode) then the address of the
                                        ; last used bit in the external memory is stored in the toc (in iicalow, iicahigh and
                                        ; iicchip/bitp, so each record in the toc uses 10 bytes:
                                        ;   9           high byte       upper nibble = bitpointer, lower nibble = iicchip
                                        ;   8           middle byte     iicahigh
                                        ;   7           low byte        iicalow
                                        ;   6           year            start time
                                        ;   5           month            ,,
                                        ;   4           date             ,,
                                        ;   3           day              ,,
                                        ;   2           hours            ,,
                                        ;   1           minutes          ,,
                                        ;   0           seconds          ,,

                                        ; BLOCK SIZE - during logging all measured data values are written to the external eeprom and
                                        ; the actual writes happen in eeprom block write size (64 bytes, the eeprom datasheet calls
                                        ; them pages, but to avoid confusion with pic memory pages we will call them blocks)
                                        ; to maximize eeprom lifetime and data throughput, the data is first buffered in pic registers
                                        ; and will only written to the external eeprom when the 64 byte size has been reached or when
                                        ; the command MEM_CLOSE is executed
                                        ; the 64 byte block buffer is in bank 1 from 2F to 6E hex, 6F hex is used as overflow
                                        ; there are 512 blocks per eeprom chip (9 bit number), in total there are 4096 blocks
                                        ; the following registers hold the first available free block in the external eeprom:
                                        ; mem_alow      here value is multiple of 64 to always point to start of a block
                                        ; mem_ahigh     maximum value is 127 since each memory chip is only 32 kbytes in size
                                        ; mem_chip      value from zero to seven, we have eight eeprom chips

                                        ; START POSITION - records always start at the beginning of a new eeprom block (64 bytes)
                                        ; and are NOT stored directly after the bits of the first record to make the download routine
                                        ; to the computer easier and to prolong the external eeprom lifetime
                                        ; the start position for any new record is calculated by the MEM_OPEN command
                                        ; note: the start address of the first record is always zero

                                        ; WRITES - the following commands are used to store the recorded data in the external
                                        ; eeprom chips, there are eight chips of 32k bytes each (total 2^21=2097152 bits)
                                        ; MEM_ADD1              digital channels: mark & brake
                                        ; MEM_ADD8              speed
                                        ; MEM_ADD10             analog channels
                                        ; MEM_ADD14             rpm
                                        ; writes are not verified by a read !
                                        ; after each add command the value of the memfull bit in the flags register
                                        ; indicates if there is any room in the memory left, this memfull flag is set at the page
                                        ; before the last page so the close command can still write the very last block to the memory

                                        ; COMPRESSION - data is stored in compressed form, meaning that a 9 bit value will be stored
                                        ; as exactly nine bits in the external eeprom, directly after any previously stored bits

;--------------------------------------------------------------------------------------------------------------------------------------

MEM_CLEAR       movlw   @tocstart       ; clear all toc bytes, both the address and the starttime bytes
                bank2                   ; location of first byte in table of contents
                movwf   iee_address     ; address for internal eeprom writes should be here
MEM_CLEAR_LOOP  clrw                    ; data for internal eeprom writes should be in w register
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w, bank0 return
                bank2                   ; iee_address is in bank2
                incfsz  iee_address,f   ; point to next byte and skip when all are done
                goto    MEM_CLEAR_LOOP  ; go clear next byte
                movlw   d'15'           ; eeprom address for error flags register
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                clrf    errors          ; clear error flags register
                clrw                    ; clear all error flags in eeprom as well,
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w, bank0 return
                bank3                   ;
                clrf    num_records     ; there are no records available anymore
                clrf    current_rec     ; there is no record selected for download anymore
                bcf     flags2,memfull  ; clear flag indicating the table of contents is now empty
                return                  ; return in bank3

;--------------------------------------------------------------------------------------------------------------------------------------

MEM_OPEN                                ; when both the external memory and the table of contents are not full then:
                                        ;
                                        ; 1. get the new values for:
                                        ;       mem_alow
                                        ;       mem_ahigh
                                        ;       mem_chip
                                        ;       bitpointer
                                        ;       regpointer
                                        ;       toc_pointer
                                        ;       memfull flag (set when memory is full, registers above will contain junk)
                                        ;
                                        ; 2. initialize (clear): blockbuff00
                                        ;
                                        ; 3. write the current time as starttime (seven bytes) into toc


                movlw   d'255'          ; position of last byte in table of contents, we start at the top and work our way down
                bank1                   ; empty locations will contain three zero values, first try three most upper bytes
                movwf   toc_pointer     ; point to the top location
MEM_OPEN_SEARCH movf    toc_pointer,w   ; get copy of tocpointer value, needed in loop
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank1                   ; pointer registers and eeprom block write buffer are all in bank 1
                movwf   mem_chip        ; store value for later use, but remember this value includes bitpointer in upper nibble
                decf    toc_pointer,f   ; point to lower location in toc
                movf    toc_pointer,w   ; copy the value 
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank1                   ; pointer registers and eeprom block write buffer are all in bank 1
                movwf   mem_ahigh       ; store value for later use
                decf    toc_pointer,f   ; point to lower location in toc
                movf    toc_pointer,w   ; copy the value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank1                   ; pointer registers and eeprom block write buffer are all in bank 1
                movwf   mem_alow        ; store value for later use, check all three values of this record to see if they are all zero
                iorwf   mem_ahigh,w     ; since this indicates that this record location is not in use yet,
                iorwf   mem_chip,w      ; so it can be used in the next logging event
                skpz                    ; are all three values zero, meaning this location is not used ?
                goto    MEM_OPEN_INUSE  ; no, use this location to get address, then point to next location which we know is empty
MEM_OPEN_EMTEST movlw   d'7'            ; yes, go see if the toc is completely empty or if there is already another record
                subwf   toc_pointer,f   ; point to start of time and date bytes for this record
                movlw   @tocstart       ; if toc is empty then we now point to the start of the toc
                subwf   toc_pointer,w   ; see if this is the first record in the toc
                skpnz                   ; will this be the first record in the toc ?
                goto    MEM_OPEN_1STREC ; yes, go set address of first record to zero to point to bottom of external eeprom memory
MEM_OPEN_DONEXT decf    toc_pointer,f   ; no, there are lower locations we should test
                goto    MEM_OPEN_SEARCH ; do loop and try next


MEM_OPEN_INUSE  movlw   d'255' - d'2'   ; when toc is full this is where the pointer will be
                subwf   toc_pointer,w   ; test the value of tocpointer, it the pointer at the last record position
                skpnz                   ; is the current pointer value valid ?
                goto    MEM_OPEN_TOCFUL ; no, stop looking, the toc is full
MEM_OPEN_POINT  movlw   d'3'            ; point to first time and date byte of next record we know is free to use 
                addwf   toc_pointer,f   ; since we have started at the top of the toc

MEM_OPEN_CALC   movlw   b'11000000'     ; use the end address of a previous record to calculate the start of the next record
                andwf   mem_alow,f      ; find the start of the last used block by stripping the lower six bits from the low byte
                movlw   d'64'           ; this value is the size of one block
                addwf   mem_alow,f      ; add one block to point to the next block, which is not used yet
                skpnc                   ; did we get an overflow of the lower byte ?
                incf    mem_ahigh,f     ; yes, also increase high byte
                btfsc   mem_ahigh,7     ; no, did we get an overflow of the lower seven bits of the high byte (we use 32k eeproms) ?
                incf    mem_chip,f      ; yes, also increase the chip select byte
                btfsc   mem_ahigh,7     ; no, again, did we get an overflow of the lower seven bits of high byte (we use 32k eeproms) ?
                bcf     mem_ahigh,7     ; yes, then reset this bit
                movlw   b'00001111'     ; no, the pointer to the last used bit was stored in upper nibble of the chip select byte
                andwf   mem_chip,f      ; we only want the chip select value so strip this nibble
                btfsc   mem_chip,3      ; see if are we pointing to a non-existing eeprom chip (there are eight eeprom chips)
                goto    MEM_OPEN_MEMFUL ; yes, we cannot use this non-existing location
MEM_OPEN_TEST   movlw   d'7'            ; no, chip number seven is the last chip
                subwf   mem_chip,w      ; see if we are using the last chip
                skpz                    ; are we pointing to the last chip ?
                goto    MEM_OPEN_WRTIME ; no, the address is ok
                movlw   d'127'          ; yes, this is the number for last four pages
                subwf   mem_ahigh,w     ; see if we are already there
                skpnz                   ; are we using one of the last four pages ?
                goto    MEM_OPEN_MEMFUL ; yes, we do not want to use this location
                goto    MEM_OPEN_WRTIME ; no, the address is ok


MEM_OPEN_1STREC clrf    mem_alow        ; yes, since there are no records yet we will start at address zero
                clrf    mem_ahigh       ; clear all address pointer bytes
                clrf    mem_chip        ; and start at the bottom of chip zero
MEM_OPEN_WRTIME call    IIC_RDCLOCK     ; read the time and date bytes to buffer in bank2 (from 68..6E hex), bank1 return
                bsf     status,irp      ; make sure upper bit in bank address is one (select bank 2 and 3)
                movlw   h'68'           ; the time and date bytes were put here
                movwf   fsr             ; point to this address
MEM_OPEN_WRLOOP bank1                   ; switch to bank1 because of loop
                movf    toc_pointer,w   ; get a copy of the tocpointer value
                bank2                   ; register is in bank2
                movwf   iee_address     ; the address byte for internal eeprom write operations
                movf    indf,w          ; get the data value for this address
                call    IEE_WRITE       ; do the actual write operation, return in bank0
                bank1                   ;
                incf    toc_pointer,f   ; point to next position in toc, after all time writes value will be at first address position
                incf    fsr,f           ; point to the next time and date byte
                movlw   h'6F'           ; position past the last time and date byte
                subwf   fsr,w           ; check if all byte have been written
                skpz                    ; have we written all bytes ?
                goto    MEM_OPEN_WRLOOP ; no, repeat until all bytes have been written to the toc
MEM_OPEN_INIT   clrf    blockbuff00     ; yes, clear first byte of blockbuffer as we use logical or operation to add new bits to buffer
                clrf    bitpointer      ; clear pointer for any new write operations, point to first bit
                movlw   blockbuff00     ; location of first byte in block buffer
                movwf   regpointer      ; use value to set register pointer to start of blockbuffer
                bcf     flags2,memfull  ; clear flag indicating the external eeproms are not full
                return                  ; return in bank1


MEM_OPEN_TOCFUL bsf     flags2,tocfull  ; set flag indicating the table of contents is full
MEM_OPEN_MEMFUL bsf     flags2,memfull  ; set flag indicating the memory is full
                return                  ; return in bank1


;--------------------------------------------------------------------------------------------------------------------------------------

MEM_WRITEBLOCK                          ; write of block buffer to external eeprom, update buffer pointers and memory address pointer
                                        ; block buffer should contain the data
                                        ; set the memfull flag when the page before the last page is reached, this is done to notify
                                        ; to the logging loop that recording should be stopped immediately to allow for a proper
                                        ; record file ending (the last page will only be used partially)

                                        ; the following registers should hold the address in the external eeprom:
                                        ; mem_alow      this value is always a multiple of 64 to point to the start of a block
                                        ; mem_ahigh     maximum value is 127 since each memory chip is only 32 kbytes in size
                                        ; mem_chip      value from zero to seven, we have eight eeprom chips


                movlw   blockbuff00     ; location of first byte in block buffer
                movwf   regpointer      ; reset the register pointer for following write events
                btfsc   mem_chip,3      ; test chip number validity, is there still room in any of the eight external eeproms ? 
                return                  ; no, return without writing data or updating pointers, the memfull flag has been set already                   
                movf    mem_alow,w      ; get address of the next available free block in the memory, select address low byte
                movwf   iicalow         ; copy value to iic write routine
                movf    mem_ahigh,w     ; select address high byte
                movwf   iicahigh        ; copy value to iic write routine
                movf    mem_chip,w      ; select chip
                movwf   iicchip         ; copy value to iic write routine
                call    IIC_WRBLOCK     ; yes, do the actual write operation and copy the block into the external eeprom
                movlw   d'64'           ; now update all adresses for following write operations, this is size of one block
                bank1                   ; we will add one block to the address
                addwf   mem_alow,f      ; try to point to next block in same chip, low byte of pointer
                skpc                    ; should we also update high byte of pointer ?
                return                  ; no, we're done
                incf    mem_ahigh,f     ; yes, increase high byte of pointer
                btfsc   mem_ahigh,7     ; is there any room left in this chip ?
                goto    MEM_WRBL_NEXT   ; no, go reset high address byte, we know low byte has wrapped around and is zero
MEM_WRBL_TEST   movlw   d'7'            ; go see if we have to set memfull flag, chip number seven is the last chip
                subwf   mem_chip,w      ; see if we are using the last chip
                skpz                    ; are we pointing to the last chip ?
                return                  ; no, we're done
                movlw   d'127'          ; yes, this is the number for last four pages
                subwf   mem_ahigh,w     ; see if we are already there
                skpnz                   ; are we using one of the last four pages ?
                bsf     flags2,memfull  ; yes, the external eeproms are almost full, set flag to allow for the very last block write
                return                  ; no, we're done, bank1 return


MEM_WRBL_NEXT   clrf    mem_ahigh       ; no, reset high address byte, we know low byte has wrapped around and is zero
                incf    mem_chip,f      ; point to next chip
                return                  ; done




;--------------------------------------------------------------------------------------------------------------------------------------


MEM_ADD1                                ; write one bit to the external eeprom
                                        ; actual writes are done when the 64 byte block buffer in pic is full
                                        ; this is done to optimize eeprom lifetime
                                        ; input = bit zero of w register (actually ior of all bits)


                bank1                   ; block buffer pointer registers and eeprom block write buffer are all in bank 1
                bcf     status,irp      ; make sure upper bit in address is zero (select register bank 0 and 1)
                iorlw   d'00000000'     ; see what value the bit is
                skpnz                   ; is input bit value a zero ?
                goto    MEM_ADD1_ZERO   ; yes, skip bit set code
MEM_ADD1_ONE    movf    regpointer,w    ; no, get the value of the pointer to the register where we will put the bit
                movwf   fsr             ; use indirect addressing
                movf    bitpointer,w    ; convert 3 bit number of bitpointer to eight bit mask, for example 010>00000100
                andlw   b'00000011'     ; first strip upper six bits and continue with only two bits
                movwf   bitmask         ; store the result
                incf    bitmask,w       ; increase by one
                btfsc   bitmask,1       ; is the first bit in mask a one ?
                iorwf   bitmask,f       ; yes, adjust result 
                incf    bitmask,f       ; no, increase by one
                btfsc   bitpointer,2    ; was bitpointer value larger than three, should we have got a bit in upper nibble ?
                swapf   bitmask,f       ; yes, simply swap nibbles
                movf    bitmask,w       ; no, result is eight bit mask
                iorwf   indf,f          ; use OR operation to set the bit
MEM_ADD1_ZERO   nop                     ; zero bit, we have to do nothing since the new bytes are always cleared
MEM_ADD1_INC    incf    bitpointer,f    ; be ready for next push so increase pointer to point to next bit
                btfss   bitpointer,3    ; has the bitcounter reached value 8 (bit 3 is now set) ?
                return                  ; no, done
                clrf    bitpointer      ; yes, so we move on to the next byte, let the bitpointer point to the first bit again
                incf    regpointer,f    ; increase the register pointer by one 
                movlw   blockbuff63 + 1 ; one position past buffer, overflow value
                subwf   regpointer,w    ; see if the register pointer has arrived at this overflow register
                skpnc                   ; is the 64 byte block buffer full ?
                call    MEM_WRITEBLOCK  ; yes, write block buffer to external eeprom and reset register pointer, bank1 return
                movf    regpointer,w    ; no, get a copy of the register pointer
                movwf   fsr             ; use this copy to set indirect file pointer to point to this location
                clrf    indf            ; clear every new byte
                return                  ; done


;--------------------------------------------------------------------------------------------------------------------------------------


MEM_ADD8                                ; write eight bits (one byte) to the external eeprom
                                        ; actual writes are done when the 64 byte block buffer in pic is full
                                        ; this is done to optimize eeprom lifetime
                                        ; the byte should be in numlow register 
                                        ; numlow and nummiddle content is not changed
                                        ; databyte0 and databyte1 contents are destroyed

MEM_ADD8_SELECT bank1                   ; block buffer pointer registers and eeprom block write buffer are all in bank 1
                bcf     status,irp      ; make sure upper bit in address is zero (select register bank 0 and 1)
                movf    numlow,w        ; copy value
                movwf   databyte0       ;
                movf    regpointer,w    ; get the register location where we will put the data bits
                movwf   fsr             ; use indirect addressing
                clrf    databyte1       ; use databyte1 as shift register or to clear new locations
                movf    bitpointer,w    ; get the position of the first free bit in this register
                skpnz                   ; is bit number zero the first free bit ?
                goto    MEM_ADD8_POS0   ; yes, so we can copy the byte directly and don't have to shift any of the bits 
MEM_ADD8_POS1_7 movwf   bitcounter      ; no, we need to shift the bits left, copy value of bitpointer to the loopcounter
                clrc                    ; we only need to clear carry once since databyte1 has been cleared
MEM_ADD8_LOOP   rlf     databyte0,f     ; shift the bits
                rlf     databyte1,f     ; shift the bits
                decfsz  bitcounter,f    ; are all the bits in the right position ?
                goto    MEM_ADD8_LOOP   ; no, go do another shift
MEM_ADD8_POS0   movf    databyte0,w     ; yes, add the lower bits to the currently selected register of the block buffer
                iorwf   indf,f          ; we use the or operation to add the new bits
                incf    regpointer,f    ; set pointer to the next byte in the buffer
                movlw   blockbuff63 + 1 ; pointing past buffer, overflow value
                subwf   regpointer,w    ; see if the pointer has arrived at this overflow register
                skpnc                   ; is the 64 byte block buffer full ? 
                call    MEM_WRITEBLOCK  ; yes, write block buffer contents to external eeprom and reset register pointer
                movf    regpointer,w    ; no, get the (updated) register location where we will put the data bits
                movwf   fsr             ; use indirect addressing
                movf    databyte1,w     ; get the upper bits or clear byte
                movwf   indf            ; store value
                return                  ; done, we don't have to update bitpointer since we added exactly eight bits


;--------------------------------------------------------------------------------------------------------------------------------------


MEM_ADD10                               ; write ten bits to the external eeprom

                                        ; actual writes are done when the 64 byte block buffer in pic is full
                                        ; this is done to optimize eeprom lifetime
                                        ; the bits should be in numlow and nummiddle register 
                                        ; numlow and nummiddle content stays unchanged
                                        ; databyte0, databyte1 and databyte2 content is destroyed 

MEM_ADD10_SEL   bank1                   ; block buffer pointer registers and eeprom block write buffer are all in bank 1
                bcf     status,irp      ; make sure upper bit in address is zero (select register bank 0 and 1)
                movf    numlow,w        ; copy value
                movwf   databyte0       ;
                movf    nummiddle,w     ; copy value
                movwf   databyte1       ;
                movlw   b'00000011'     ; make sure we use a 10 bit number
                andwf   databyte1,f     ; strip any excess bits
                clrf    databyte2       ; use databyte2 as shift register or to clear new locations
                movf    regpointer,w    ; get the register location where we will put the data bits
                movwf   fsr             ; use indirect addressing
                movf    bitpointer,w    ; get the position of the first free bit in this register
                skpnz                   ; is bit number zero the first free bit ?
                goto    MEM_ADD10_POS0  ; yes, so we can copy the bytes directly and don't have to shift any of the bits 
MEM_ADD10_POS17 movwf   bitcounter      ; no, we need to shift the bits left, copy value of bitpointer to the loopcounter
                clrc                    ; we only need to clear carry once since databyte1 and databyte2 have been cleared
MEM_ADD10_LOOP  rlf     databyte0,f     ; shift the bits
                rlf     databyte1,f     ; shift the bits
                rlf     databyte2,f     ; shift the bits
                decfsz  bitcounter,f    ; are all the bits in the right position ?
                goto    MEM_ADD10_LOOP  ; no, go do another shift
MEM_ADD10_POS0  movf    databyte0,w     ; yes, add the lower bits to the currently selected register of the block buffer
                iorwf   indf,f          ; we use the or operation to add the new bits
                incf    regpointer,f    ; set pointer to the next byte in the buffer
                movlw   blockbuff63 + 1 ; pointing past buffer, overflow value
                subwf   regpointer,w    ; see if the pointer has arrived at this overflow register
                skpnc                   ; is the 64 byte block buffer full ? 
                call    MEM_WRITEBLOCK  ; yes, write block buffer contents to external eeprom and reset register pointer
                movf    regpointer,w    ; no, get the (updated) register location where we will put the data bits
                movwf   fsr             ; use indirect addressing
                movf    databyte1,w     ; no, get (part of) the upper bits
                movwf   indf            ; store these bits
                incf    bitpointer,f    ; update the position of the bitcounter
                incf    bitpointer,f    ; new bitpointer value = (bitpointer + 10) MOD 8 = bitpointer + 2
                btfss   bitpointer,3    ; was the start position of the bitcounter bit 6 or bit 7 ?
                return                  ; no, still some room left after storing 10 bit value in two registers, done
MEM_ADD10_THREE bcf     bitpointer,3    ; yes, no room left or using three registers, update register pointer, clear overflow bit
                incf    regpointer,f    ; set pointer to the next byte in the buffer
                movlw   blockbuff63 + 1 ; pointing past buffer, overflow value
                subwf   regpointer,w    ; see if the pointer has arrived at this overflow register
                skpnc                   ; is the 64 byte block buffer full ? 
                call    MEM_WRITEBLOCK  ; yes, write block buffer contents to external eeprom and reset register pointer
                movf    regpointer,w    ; get the (updated) register location where we will put the data bits
                movwf   fsr             ; use indirect addressing
                movf    databyte2,w     ; no, get the uppermost bit
                movwf   indf            ; store this bit
                return                  ; done


;--------------------------------------------------------------------------------------------------------------------------------------

MEM_ADD14                               ; write fourteen bits to the external eeprom

                                        ; actual writes are done when the 64 byte block buffer in pic is full
                                        ; this is done to optimize eeprom lifetime
                                        ; the bits should be in numlow and nummiddle register 
                                        ; numlow and nummiddle content stays unchanged
                                        ; databyte0, databyte1 and databyte2 content is destroyed 


MEM_ADD14_SEL   bcf     status,irp      ; make sure upper bit in address is zero (select register bank 0 and 1)
                movf    numlow,w        ; copy value
                bank1                   ; block buffer pointer registers and eeprom block write buffer are all in bank 1
                movwf   databyte0       ;
                movf    nummiddle,w     ; copy value
                andlw   b'00111111'     ; make sure we use a 14 bit number
                movwf   databyte1       ; store result
                movf    regpointer,w    ; get the register location where we will put the data bits
                movwf   fsr             ; use indirect addressing
                bcf     status,irp      ; make sure upper bit in address is zero (select register bank 0 and 1)
                clrf    databyte2       ; use databyte2 as shift register or to clear new locations
                movf    bitpointer,w    ; get the position of the first free bit in this register
                skpnz                   ; is bit number zero the first free bit ?
                goto    MEM_ADD14_POS0  ; yes, so we can copy the bytes directly and don't have to shift any of the bits 
MEM_ADD14_POS17 movwf   bitcounter      ; no, we need to shift the bits left, copy value of bitpointer to the loopcounter
                clrc                    ; we only need to clear carry once since we know value of databyte1 and databyte2
MEM_ADD14_LOOP  rlf     databyte0,f     ; shift the bits
                rlf     databyte1,f     ; shift the bits
                rlf     databyte2,f     ; shift the bits
                decfsz  bitcounter,f    ; are all the bits in the right position ?
                goto    MEM_ADD14_LOOP  ; no, go do another shift
MEM_ADD14_POS0  movf    databyte0,w     ; yes, add the lower bits to the currently selected register of the block buffer
                iorwf   indf,f          ; we use the or operation to add the new bits
                incf    regpointer,f    ; set pointer to the next byte in the buffer
                movlw   blockbuff63 + 1 ; pointing past buffer, overflow value
                subwf   regpointer,w    ; see if the pointer has arrived at this overflow register
                skpnc                   ; is the 64 byte block buffer full ? 
                call    MEM_WRITEBLOCK  ; yes, write block buffer contents to external eeprom and reset register pointer
                movf    regpointer,w    ; no, get the (updated) register location where we will put the data bits
                movwf   fsr             ; use indirect addressing
                movf    databyte1,w     ; no, get (part of) the upper bits
                movwf   indf            ; store these bits
                movlw   d'6'            ; update the position of the bitcounter
                addwf   bitpointer,f    ; new bitpointer value = (bitpointer + 14) MOD 8 = bitpointer + 6
                btfss   bitpointer,3    ; was the start position of the bitcounter bit 2 or greater ?
                return                  ; no, still some room left after storing 14 bit value in two registers, done
MEM_ADD14_THREE bcf     bitpointer,3    ; yes, no room left or using three registers, update register pointer, clear overflow bit
                incf    regpointer,f    ; set pointer to the next byte in the buffer
                movlw   blockbuff63 + 1 ; pointing past buffer, overflow value
                subwf   regpointer,w    ; see if the pointer has arrived at this overflow register
                skpnc                   ; is the 64 byte block buffer full ? 
                call    MEM_WRITEBLOCK  ; yes, write block buffer contents to external eeprom and reset register pointer
                movf    regpointer,w    ; get the (updated) register location where we will put the data bits
                movwf   fsr             ; use indirect addressing
                movf    databyte2,w     ; no, get the uppermost bits
                movwf   indf            ; store these bits
                return                  ; done

;--------------------------------------------------------------------------------------------------------------------------------------


MEM_CLOSE                               ; write any bytes that are still in the blockbuffer to the external eeprom, unused bytes are
                                        ; cleared first, update the table of contents with the address of the last used bit
                                        ; this routine may also be called when we didn't store anything yet, when the memory is full
                                        ; or when the table of contents is already full and we are just aborting the logging
                                        ; note: during the add commands we use a pointer that points to the next free bit,
                                        ; but we store the position of last used bit in the toc


MEM_CL_CHECK1   nop                     ; **** check if we have added anything to store at all
MEM_CL_CHECK2   btfsc   flags2,tocfull  ; is the table of contents full and should we ignore the call to this subroutine ?
                return                  ; yes, return without updating the table of contents
MEM_CL_CHECKOK  movlw   blockbuff00     ; no, start of block buffer location
                bank1                   ; pointer registers and eeprom block write buffer are all in bank 1
                subwf   regpointer,w    ; see if we are still at the first byte of the block buffer
                skpz                    ; did we write anything into the buffer yet ?
                goto    MEM_CL_CLRBUFF  ; yes, go clear rest of bytes in buffer         
                movf    bitpointer,w    ; no, also get value of bitpointer
                skpnz                   ; are we sure the buffer is completely empty ?
                goto    MEM_CL_DECPOINT ; yes, we don't have to write this block to the external eeprom
MEM_CL_CHECKP   movlw   blockbuff63     ; no, position of last byte in buffer
                subwf   regpointer,w    ; see if we are pointing to the last byte in the buffer
                skpnz                   ; are we pointing to the last byte ?
                goto    MEM_CL_ADDR     ; yes, there are no bytes to be cleared, skip clear routine
MEM_CL_CLRBUFF  incf    regpointer,w    ; no, start at register pointer location plus one with clearing bytes
                movwf   fsr             ; use indirect addressing
                bcf     status,irp      ; make sure upper bit in address is zero (select register bank 0 and 1)
MEM_CL_CLRLOOP  clrf    indf            ; clear byte of buffer, we want to clear all the rest of the buffer
                movlw   blockbuff63     ; position of last byte in buffer
                incf    fsr,f           ; point to next byte in buffer for when we do loop
                subwf   fsr,w           ; see if the pointer has arrived at last position
                skpz                    ; have we done the all of the 64 byte block buffer ? 
                goto    MEM_CL_CLRLOOP  ; no, do loop to clear next byte
MEM_CL_ADDR     movf    mem_alow,w      ; yes, write contents of block buffer to external eeprom, use address of next free block
                movwf   iicalow         ; copy value to iic write routine
                movf    mem_ahigh,w     ; select address high byte
                movwf   iicahigh        ; copy value to iic write routine
                movf    mem_chip,w      ; select chip
                movwf   iicchip         ; copy value to iic write routine
                call    IIC_WRBLOCK     ; do the actual write operation and copy the block into the external eeprom, bank1 return
MEM_CL_DECPOINT decf    bitpointer,f    ; don't point to next free bit anymore but to the last used bit, decrement pointer one bit
                movlw   blockbuff00     ; offset between zero and start address of blockbuffer in bank1
                subwf   regpointer,w    ; get lower six bits of the lower address byte (end position can be anywhere in a block)
                iorwf   mem_alow,f      ; merge with upper two bits of low address byte (which always holds start of a block)
                comf    bitpointer,w    ; since decf does not change the carry we use the complement to see if the result is negative 
                skpnz                   ; is the complement zero, meaning the result of the decrement was a negative value ?
                decf    mem_alow,f      ; yes, decrease the lower address byte by one because we have to borrow
                movlw   b'00000111'     ; no, when the result from the decrement of the bitpointer is negative then
                andwf   bitpointer,f    ; set the bitpointer to its maximum value instead
                comf    mem_alow,w      ; since decf does not change the carry we use the complement to see if the result is negative 
                skpnz                   ; was the result of the decrement of the low byte negative ?
                decf    mem_ahigh,f     ; yes, decrease the higher address byte by one because we have to borrow
                comf    mem_ahigh,w     ; since decf does not change the carry we use the complement to see if the result is negative 
                skpnz                   ; is the complement zero, meaning the result of the decrement was a negative value ?
                decf    mem_chip,f      ; yes, borrow from the chip select byte
                movlw   b'01111111'     ; no, when the result from the decrement of the high address byte is negative then
                andwf   mem_ahigh,f     ; set the high address byte to its maximum value instead (we use 15 bit/32kB eeproms)
MEM_CL_WRITETOC movf    toc_pointer,w   ; get the location in the toc where we should store the address of the last used bit
                bank2                   ; so we can determine where data from one record stops and a new record begins
                movwf   iee_address     ; copy value for use in to pic eeprom write routine
                bank1                   ; pointer registers and eeprom block write buffer are all in bank 1
                movf    mem_alow,w      ; get the low byte of the address pointer
                call    IEE_WRITE       ; write one byte to pic eeprom, address in iee_address(bank2), data in w register, bank0 return
                bank2                   ;
                incf    iee_address,f   ; point to next byte in toc
                bank1                   ;
                movf    mem_ahigh,w     ; get the high byte of the address pointer
                call    IEE_WRITE       ; write one byte to pic eeprom, address in iee_address(bank2), data in w register, bank0 return
                bank2                   ;
                incf    iee_address,f   ; point to next byte in toc
                bank1                   ;
                swapf   bitpointer,f    ; move bitpointer bits to high nibble
                movf    mem_chip,w      ; since the bitpointer and the chip select value are stored in the same byte
                iorwf   bitpointer,w    ; combine values of bitpointer and chip select
                call    IEE_WRITE       ; write one byte to pic eeprom, address in iee_address(bank2), data in w register, bank0 return
                return                  ; done, return in bank0


;--------------------------------------------------------------------------------------------------------------------------------------

MEM_USAGE       nop                     ; **** get the used space of the external eeprom memory in percent into w register
                nop                     ; **** read end of last record position, do division to get percentage
                movlw   d'100'          ; **** dummy value
                return                  ; **** return in bank?


;--------------------------------------------------------------------------------------------------------------------------------------

MEM_GETNUMRECS                          ; determine the number of records currently present in the table of contents
                                        ; value is stored in num_records in bank3 
                                        ; registers used: toc_pointer, toc_test, num_records
                                        ; returns in bank1 or in bank3

                movlw   d'255'          ; start with number of records is -1
                bank3                   ;
                movwf   num_records     ;
                movlw   @tocstart + d'7'        ; start address of address bytes of first record in table of contents in internal eeprom
                bank1                   ; 
                movwf   toc_pointer     ; point to this location
MEM_GETNRLOOP   bank3                   ;
                incf    num_records,f   ; increment number of records by one
                movlw   @tocstart       ; to test for pointer overflow after loop increment see if value of pointer
                bank1                   ;
                subwf   toc_pointer,w   ; has not wrapped around since the table of contents ends at address 255
                skpc                    ; is the current pointer value valid ?
                return                  ; no, the pointer has wrapped around, exit with number of 20 records, return in bank3
                movf    toc_pointer,w   ; yes, get value of pointer
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank1                   ;
                movwf   toc_test        ; store value to test if all three bytes are zero
                incf    toc_pointer,f   ; pointer to the next location in the toc
                movf    toc_pointer,w   ; use copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank1                   ;
                iorwf   toc_test,f      ; also test this byte for zero value
                incf    toc_pointer,f   ; pointer to the next location in the toc
                movf    toc_pointer,w   ; use copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank1                   ;
                iorwf   toc_test,f      ; also test this byte for zero value
                movlw   d'8'            ; records in toc each take ten bytes, we already increased pointer by two
                addwf   toc_pointer,f   ; point to next record in toc in case we do loop
                movf    toc_test,w      ; now we have to test again if all three bytes are zero
                skpz                    ; is the value of all three byte zero ?
                goto    MEM_GETNRLOOP   ; no, this location is already taken, go see if next location is free
                return                  ; yes, return in bank1

;--------------------------------------------------------------------------------------------------------------------------------------

MEM_SELECTREC                           ; reset the block download pointers pointer_low/high/chip and endpoint_low/high/chip for the
                                        ; given record number, update current_rec
                                        ; record number should be in w register
                                        ; the pointer values are rounded to block size since we can only download complete blocks

                bank3                   ; this routine is called only with a valid record selection, no need to check record number
                movwf   current_rec     ; the new record number selection is in w register
                movlw   @tocstart +d'7' ; start off with pointing to the first address byte of the first record in the toc
                bank1                   ; we will have to look up the right address
                movwf   toc_pointer     ; so we will use this value in the pointer calculation which follows
                bank3                   ; this routine is called only with a valid record selection, no need to check record number
                decf    current_rec,w   ; copy selected record number minus one to a loopcounter, value is now 0..19
                movwf   rec_loopcntr    ; we may use this counter in the following loop
                skpnz                   ; is the value zero meaning record number one is currently selected ?
                goto    MEM_SELECTR_ZER ; yes, this is a special case, the starting address is not in the toc since it is always zero
MEM_SELECTR_INC bank3                   ; first time we get here is with records 2..20, loopcounter value 1..19
                decf    rec_loopcntr,f  ; count every record
                skpnz                   ; should we increase the pointer to the next record ?
                goto    MEM_SELECTR_STA ; no, get values
                movlw   d'10'           ; yes, we repeat until the pointer has the right value
                bank1                   ; each record uses ten bytes in the table of contents
                addwf   toc_pointer,f   ; point to the next record
                goto    MEM_SELECTR_INC ; repeat until the pointer has the right value


MEM_SELECTR_STA bank1                   ; no, read the values from the toc into the pointer registers
                movf    toc_pointer,w   ; use the value of the tocpointer
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   pointer_low     ;
                bank1                   ;
                incf    toc_pointer,f   ; also for the next byte
                movf    toc_pointer,w   ; get copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   pointer_high    ;
                bank1                   ;
                incf    toc_pointer,f   ; and for this byte
                movf    toc_pointer,w   ; get copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   pointer_chip    ; this value also includes bitpointer
MEM_SELECTR_CAL movlw   b'11000000'     ; strip the lower six bits from pointer_low value
                andwf   pointer_low,f   ; since we want to start at beginning of the next block
                movlw   d'64'           ; this value is the size of one block
                addwf   pointer_low,f   ; add one block to current block pointer
                skpnc                   ; did we get an overflow of the lower byte ?
                incf    pointer_high,f  ; yes, also increase high byte of pointer
                btfsc   pointer_high,7  ; no, did we get an overflow of the lower seven bits of the high byte (we use 32k eeproms) ?
                incf    pointer_chip,f  ; yes, also increase the chip select byte
                btfsc   pointer_high,7  ; no, again, did we get an overflow of the lower seven bits of high byte (we use 32k eeproms) ?
                bcf     pointer_high,7  ; yes, then reset this bit
                movlw   b'00001111'     ; no, the pointer to the last used bit was stored in upper nibble of the chip select byte
                andwf   pointer_chip,f  ; we only want chip select so strip bitpointer from value
                movlw   d'8'            ; point to end address positions
                bank1                   ; we already increased the pointer by two positions
                addwf   toc_pointer,f   ; now add the other eight 
                movf    toc_pointer,w   ; copy value
MEM_SELECTR_END call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   endpoint_low    ;
                movlw   b'11000000'     ; strip the lower six bits from pointer_low value
                andwf   endpoint_low,f  ; since we want to start at beginning of the block
                bank1                   ;
                incf    toc_pointer,f   ; also for the next byte
                movf    toc_pointer,w   ; get copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   endpoint_high   ;
                bank1                   ;
                incf    toc_pointer,f   ; and for this byte
                movf    toc_pointer,w   ; get copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   endpoint_chip   ; this value also includes bitpointer
                movlw   b'00001111'     ; last free bit pointer is stored in upper nibble
                andwf   endpoint_chip,f ; we only want chip select so strip bitpointer from value
                return                  ; return in bank3


MEM_SELECTR_ZER clrf    pointer_low     ; we get here in bank3
                clrf    pointer_high    ; set block pointer to zero for record number one
                clrf    pointer_chip    ; since this record always starts at the bottom of the memory
                bank1                   ;
                movf    toc_pointer,w   ; get pointer of address bytes of first record
                goto    MEM_SELECTR_END ; tocpointer was already set to right address for end of record number one


;--------------------------------------------------------------------------------------------------------------------------------------

MEM_GETTIMEDATE                         ; read the record start time and date of currently selected record from the toc and store
                                        ; these in the time and date buffer in bank2
                                        ; return in bank2

                bsf     status,irp      ; make sure upper bit in address is one (select bank 2 and 3)
                movlw   h'68'           ; get the time and date bytes in the buffer in bank2 
                movwf   fsr             ; since we have to decode them first, set pointer which we will use later
                movlw   @tocstart -d'10'        ; point to the time bytes of first record in the toc,
                bank1                   ; minus ten bytes since following addition loop runs at least once
                movwf   toc_pointer     ; use this value in the pointer calculation which follows 
                bank3                   ;
                movf    current_rec,w   ; get the currently selected record number and use this number to calculate the offset 
                movwf   rec_loopcntr    ; in the table of contents, increment value with one for proper loop end
MEM_GTDFINDLOOP movlw   d'10'           ; each record uses ten bytes in the table of contents
                bank1                   ;
                addwf   toc_pointer,f   ; point to the next record
                bank3                   ;
                decfsz  rec_loopcntr,f  ; should we repeat the addition ?
                goto    MEM_GTDFINDLOOP ; yes, repeat until the pointer has the right value
MEM_GTDCOPYLOOP bank1                   ; no, now go copy the bytes from internal eeprom to buffer
                movf    toc_pointer,w   ; use the value of the tocpointer
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                movwf   indf            ; store the data value for this address
                bank1                   ;
                incf    toc_pointer,f   ; point to next position in table of contents
                incf    fsr,f           ; point to the next time and date byte
                movlw   h'6F'           ; position past the last time and date byte
                subwf   fsr,w           ; check if all byte have been written
                skpz                    ; have we copied all bytes ?
                goto    MEM_GTDCOPYLOOP ; no, repeat until all bytes have been copied to the buffer
                return                  ; return in bank2

;--------------------------------------------------------------------------------------------------------------------------------------

MEM_GETRECSIZE                          ; store size (in bits) of currently selected record into registers numlow, nummiddle, numhigh
                                        ; this routine is called only with a valid record selection, no need to check record number


                movlw   @tocstart +d'7' ; start off with pointing to the first address byte of the first record in the toc
                bank1                   ; we will have to look up the right address for the selected record
                movwf   toc_pointer     ; use this value in the pointer calculation which follows
                bank3                   ; this routine is called only with a valid record selection, no need to check record number
                decf    current_rec,w   ; copy selected record number minus one to a loopcounter, value is now 0..19
                movwf   rec_loopcntr    ; we may use this counter in the following loop
                skpnz                   ; is the value zero meaning record number one is currently selected ?
                goto    MEM_RECSIZE_ZER ; yes, this is a special case, the starting address is not in the toc since it is always zero
MEM_RECSIZE_INC bank3                   ; first time we get here is with records 2..20, loopcounter value 1..19
                decf    rec_loopcntr,f  ; loopcounter
                skpnz                   ; should we repeat the addition ?
                goto    MEM_RECSIZE_STA ; no, we are at the right position, go use this location as start address of record
                movlw   d'10'           ; yes, we repeat until the pointer has the right value
                bank1                   ; each record uses ten bytes in the table of contents
                addwf   toc_pointer,f   ; point to the next record
                goto    MEM_RECSIZE_INC ; do loop until we get right address
MEM_RECSIZE_STA bank1                   ; no, read the values from the toc into the pointer registers
                movf    toc_pointer,w   ; use the value of the tocpointer
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   recsizea0       ; a0/a1/a2 is the smaller number (record n), b0/b1/b2 will be the larger number (record n+1)
                bank1                   ; so result is record size = b - a [bits]
                incf    toc_pointer,f   ; also for the next byte
                movf    toc_pointer,w   ; get copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   recsizea1       ;
                bank1                   ;
                incf    toc_pointer,f   ; and for this byte
                movf    toc_pointer,w   ; get copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   recsizea2       ; this value is combined chipselect (least significant nibble) and bitpointer (upper nibble)
                movlw   d'8'            ; move pointer to end address position bytes
                bank1                   ; we already increased the pointer by two positions
                addwf   toc_pointer,f   ; now add the other eight 
MEM_RECSIZE_CAL bank3                   ; we have to adjust the start address
                movlw   b'11000000'     ; use the end address of a previous record to calculate the start of the next record
                andwf   recsizea0,f     ; find the start of the last used block by stripping the lower six bits from the low byte
                movlw   d'64'           ; this value is the size of one block
                addwf   recsizea0,f     ; add one block to point to the next block, which is not used yet
                skpnc                   ; did we get an overflow of the lower byte ?
                incf    recsizea1,f     ; yes, also increase high byte
                btfsc   recsizea1,7     ; no, did we get an overflow of the lower seven bits of the high byte (we use 32k eeproms) ?
                incf    recsizea2,f     ; yes, also increase the chip select byte
                btfsc   recsizea1,7     ; no, again, did we get an overflow of the lower seven bits of high byte (we use 32k eeproms) ?
                bcf     recsizea1,7     ; yes, then reset this bit
                movlw   b'00001111'     ; no, the pointer to the last used bit was stored in upper nibble of the chip select byte
                andwf   recsizea2,f     ; we only want the chip select value so strip this nibble
MEM_RECSIZE_END bank1                   ; select bank since we may come from bank3 (record number is zero code below)
                movf    toc_pointer,w   ; get pointer of address bytes of first record
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   recsizeb0       ;
                bank1                   ;
                incf    toc_pointer,f   ; also for the next byte
                movf    toc_pointer,w   ; get copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   recsizeb1       ;
                bank1                   ;
                incf    toc_pointer,f   ; and for this byte
                movf    toc_pointer,w   ; get copy of value
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                bank3                   ;
                movwf   recsizeb2       ; this value also includes bitpointer
MEM_RECSIZE_HI  rlf     recsizeb2,w     ; the b recordsize registers hold larger address
                movwf   numhigh         ; calculate address from bitp/chipselect, high address byte and low address byte
                rlf     numhigh,f       ; store address in numlow, nummiddle and numhigh
                rlf     recsizeb0,w     ;
                movwf   numlow          ;
                rlf     recsizeb1,w     ;
                movwf   nummiddle       ;
                rlf     numhigh,f       ;
                rlf     numlow,f        ;
                rlf     nummiddle,f     ;
                rlf     recsizeb2,f     ;
                rlf     numhigh,f       ;
                rlf     numlow,f        ; alow43210 bitp210
                rlf     nummiddle,f     ; ahigh43210 alow765
                rlf     recsizeb2,w     ;
                andlw   b'00011111'     ;
                movwf   numhigh         ; 0 0 0 chip210 ahigh65
MEM_RECSIZE_LO  rlf     recsizea2,w     ; the a recordsize registers hold smaller address
                movwf   recsizeb2       ; calculate address from bitp/chipselect, high address byte and low address byte
                rlf     recsizeb2,f     ; store address in recsizeb2, recsizeb1, recsizeb0
                rlf     recsizea0,w     ;
                movwf   recsizeb0       ;
                rlf     recsizea1,w     ;
                movwf   recsizeb1       ;
                rlf     recsizeb2,f     ;
                rlf     recsizeb0,f     ;
                rlf     recsizeb1,f     ;
                rlf     recsizea2,f     ;
                rlf     recsizeb2,f     ;
                rlf     recsizeb0,f     ;
                rlf     recsizeb1,f     ;
                rlf     recsizea2,w     ;
                andlw   b'00011111'     ;
                movwf   recsizeb2       ;
MEM_RECSIZE_SUB movf    recsizeb0,w     ; now do subtraction to get record size
                subwf   numlow,f        ; store result in numlow, nummiddle and numhigh
                movf    recsizeb1,w     ;
                skpc                    ;
                incfsz  recsizeb1,w     ;
                subwf   nummiddle,f     ;
                movf    recsizeb2,w     ;
                skpc                    ;
                incfsz  recsizeb2,w     ;
                subwf   numhigh,f       ;
MEM_RECSIZE_ADD movlw   d'1'            ; because of the way we store the start and end position of a record in the table of contents
                addwf   numlow,f        ; we have to add one extra bit to the size of the record
                skpnc                   ; a record of one bit will not be found in the toc since the start and end addresses are of
                incf    nummiddle,f     ; the same value, therefore the minimum size of a record is two bits
                skpnz                   ;
                incf    numhigh,f       ;       
                return                  ; return in bank3


MEM_RECSIZE_ZER clrf    recsizea0       ; set block pointer to zero for record number one
                clrf    recsizea1       ; since this record always starts at the bottom of the memory
                clrf    recsizea2       ; 
                goto    MEM_RECSIZE_END ; tocpointer is already at right address

;--------------------------------------------------------------------------------------------------------------------------------------

MEM_RDBLOCK                             ; copy data block from external eeprom to the block buffer in bank1
                                        ; carry set when all blocks done
                                        ; return in bank1 or bank3

MEM_RDBL_CHECK2 bank3                   ;
                movf    pointer_low,w   ; check for end of record, have we sent all blocks?
                subwf   endpoint_low,w  ; subtract, first low bytes 
                movf    pointer_high,w  ; high byte
                skpc                    ; did we have to borrow ?
                incfsz  pointer_high,w  ; yes, adjust value
                subwf   endpoint_high,w ; no, subtract high bytes
                movf    pointer_chip,w  ; chip select bytes
                skpc                    ; did we have to borrow ?
                incfsz  pointer_chip,w  ; yes, adjust value
                subwf   endpoint_chip,w ; no, subtract chip select bytes
                skpnc                   ; do we have a valid address ?
                goto    MEM_RDBL_COPY   ; yes, go copy the block from external eeprom to buffer in bank1
                setc                    ; no, set carry as indication that we have already read all blocks of this record
                return                  ; carry has been set, return in bank3


MEM_RDBL_COPY   bank3                   ; get block pointer
                movf    pointer_low,w   ; address low byte
                bank1                   ;
                movwf   iicalow         ; copy value
                bank3                   ;
                movf    pointer_high,w  ; select address high byte
                bank1                   ;
                movwf   iicahigh        ; copy value
                bank3                   ;
                movf    pointer_chip,w  ; select chip
                bank1                   ;
                movwf   iicchip         ; copy value
                call    IIC_RDBLOCK     ; read 64 bytes from external eeprom, address in iicchip, iicalow and iicahigh, bank1 return
                clrc                    ; clear carry
                return                  ; return in bank1


;--------------------------------------------------------------------------------------------------------------------------------------

MEM_INC_RDBLOCK                         ; auto increment blockpointer, then copy data block from external eeprom to the block
                                        ; buffer in bank1
                                        ; carry set when all blocks done
                                        ; return in bank1 or bank3

MEM_RDBL_CHECK1 bank3                   ;
                movf    pointer_low,w   ; check for end of record, have we sent all blocks?
                subwf   endpoint_low,w  ; subtract, first low bytes 
                movf    pointer_high,w  ; high byte
                skpc                    ; did we have to borrow ?
                incfsz  pointer_high,w  ; yes, adjust value
                subwf   endpoint_high,w ; no, subtract high bytes
                movf    pointer_chip,w  ; chip select bytes
                skpc                    ; did we have to borrow ?
                incfsz  pointer_chip,w  ; yes, adjust value
                subwf   endpoint_chip,w ; no, subtract chip select bytes
                skpnc                   ; do we have a valid address ?
                goto    MEM_RDBL_INCADD ; yes, go copy the block from external eeprom to buffer in bank1
                setc                    ; no, set carry as indication that we have already read all blocks of this record
                return                  ; carry has been set, return in bank3


MEM_RDBL_INCADD movlw   d'64'           ; no, this is the size of one memory block in external eeprom
                bank3                   ;
                addwf   pointer_low,f   ; increment memory block pointer, first low byte
                skpc                    ; do we need to update high byte as well ?
                goto    MEM_RDBLOCK     ; no, go check address again, then copy block
                incf    pointer_high,f  ; yes, do so
                btfsc   pointer_high,7  ; the current eeprom chips are 32kB (15 bits address), did we get an overflow ?
                incf    pointer_chip,f  ; yes, also increase chip select byte
                bcf     pointer_high,7  ; no, we don't use bit 7 since we have 32kB eeproms, clear bit
                goto    MEM_RDBLOCK     ; go check address again, then copy block


;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------


                org     h'1000'         ; start of program memory page 2


;--------------------------------------------------------------------------------------------------------------------------------------
; RS232 subroutines for transmitting and receiving bytes and numbers (ascii strings)
;--------------------------------------------------------------------------------------------------------------------------------------


                                        ; a command byte sequence is (in ascii):
        
                                        ; <STX>         1 byte indicator for start of command, value hex 02
                                        ; <c1>          1 byte, first letter of command
                                        ; <c2>          1 byte, second letter of command
                                        ; <data bytes>  0-64 data bytes, all values accepted except ascii codes STX and ETX 
                                        ; <checksum>    1 byte checksum (see below), any hex value 00-FF
                                        ; <ETX>         1 byte indicator for end of command, value hex 03

                                        ; checksum value makes sum of all bytes from <STX> up to and including <ETX> zero

TX_BINVALUE     addlw   h'30'           ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 

TX_BYTE                                 ; transmit content of w register through RS232 (odd parity)
                                        ; checksum calculation is included
                                        ; w register content is destroyed
                                        ; returns in bank0

                bank0                   ; select bank, then calculate what the value of the parity bit is going to be:
                movwf   tx_byte         ; w holds the byte to be transmitted, store w for use after the parity calculation
                addwf   tx_checksum,f   ; use byte in checksum calculation
                movwf   tx_parity       ;
                swapf   tx_parity,w     ; use w and tx_parity registers for the calculation
                xorwf   tx_parity,f     ; calculate nibbles
                rrf     tx_parity,w     ;
                xorwf   tx_parity,f     ; at this point the parity values of the nibbles are in bit 2 and bit 0
                btfss   tx_parity,2     ; if parity one nibble is 1 then the byte parity is that of other nibble, skip ahead
                incf    tx_parity,f     ; otherwise, invert bit 0
                bank1                   ;
                bcf     txsta,tx9d      ; first assume we should reset the parity bit that will be send
                bank0                   ;
                btfss   tx_parity,0     ; was it the right decision to reset the bit ?
                goto    TX_LOOP         ; yes, go send the byte
                bank1                   ;
                bsf     txsta,tx9d      ; no, first set the parity bit and then send the byte   
                bank0                   ;
TX_LOOP         btfss   pir1,txif       ; yes, ready to send ? (bit set means transmit buffer is empty)
                goto    TX_LOOP         ; no, keep trying
TX_POLL_RTS     btfsc   portc,rts_in    ; is the PC input buffer empty, so are we allowed to send data ? (RTS serves as a CTS here)
                goto    TX_POLL_RTS     ; no, keep trying
                                        ; **** to do: we would like to have some timeout testing here....
                movf    tx_byte,w       ; yes, move character to w
                movwf   txreg           ; transmit byte
                ;nop                    ; there is a delay of one instruction cycle after writing to txreg, before txif gets cleared
                return                  ; line above, nop, is just information for of possible future changes, return in bank0

;--------------------------------------------------------------------------------------------------------------------------------------

TX_COMMANDSTART                         ; send ACK, STX and command mnemonics

                movlw   h'06'           ; Acknowledgement (ACK)
                call    TX_BYTE         ; send 'command has been accepted', return in bank0
                clrf    tx_checksum     ; start off with clear checksum value
                movlw   h'02'           ; STX
                call    TX_BYTE         ; return in bank0
                movf    rx_buffer00,w   ; location of first letter of mnemonic in RS232 buffer
                call    TX_BYTE         ;
                movf    rx_buffer01,w   ; location of second letter of mnemonic in RS232 buffer
                call    TX_BYTE         ;
                return                  ;

;--------------------------------------------------------------------------------------------------------------------------------------

TX_COMMANDEND                           ; send checksum and ETX, clear command status byte

                bank0                   ; 
                movlw   h'03'           ; ETX
                call    TX_BYTE         ; transmit, return in bank0
                movf    tx_checksum,w   ; get checksum value
                sublw   d'0'            ; calculate right value (all command bytes added should result in zero value)
                call    TX_BYTE         ; transmit, return in bank0
                bcf     flags1,command  ; now command has been handled we can clear the execute flag
                bcf     portc,cts_out   ; set CTS to re-enable data stream from computer
                bsf     portb,led_red   ; turn on red status led
                bcf     portb,led_green ; turn off green status led
                return                  ; return in bank0

;--------------------------------------------------------------------------------------------------------------------------------------

TX_NUMBER                               ; this routine transmits the 8 bit number (0-255) in the w register via RS232 as
                                        ; (up to three) ascii characters without preceding zeros (23 will be sent as '23'
                                        ; and not as '023'), transmit checksum is calculated
                                        ; w register content is destroyed
                                        ; returns in bank0

                                        ; 255   byte value
                                        ;
                                        ; 100    100
                                        ;  10     10
                                        ;   1      1

                bank0
                movwf   tx_numberl      ; store the value of the number for use in the calculation
                movlw   b'10000000'     ; use the upper bit of the counter as a flag for preceding zeros,
                movwf   tx_counter      ; 1 means skip characters
                movlw   d'100'          ; how many hundreds ?
                call    TX_DIGIT        ; send digit
                movlw   b'10000000'     ; select flag bit
                andwf   tx_counter,f    ; clear counter but keep preceding zeros flag bit
                movlw   d'10'           ; how many tens ?
                call    TX_DIGIT        ; send digit
                clrf    tx_counter      ; the last character always has to be sent so clear counter and flag bit too
                movlw   d'1'            ; how many ones ?
                goto    TX_DIGIT        ; goto instead of call because we are done here

TX_NLOOP        bcf     tx_counter,7    ; this character is non-zero, so begin sending characters
                incf    tx_counter,f    ; increment counter
TX_DIGIT        subwf   tx_numberl,f    ; subtract
                skpnc                   ; is the result negative ?
                goto    TX_NLOOP        ; no, repeat
                addwf   tx_numberl,f    ; undo negative result, we need the right value for the rest of the calculation
                btfsc   tx_counter,7    ; can we still skip the preceding zero characters ?
                return                  ; yes, skip those characters
                movlw   a'0'            ; in ascii numbers start at 30 hex
                addwf   tx_counter,w    ; so add the counter to get the right ascii code
                call    TX_BYTE         ; send the ascii character stored in register w, return in bank0
                return                  ; return in bank0

;--------------------------------------------------------------------------------------------------------------------------------------

TX_LNUMBER                              ; this routine transmits the 16 bit number (0-65535) in the registers numlow and
                                        ; nummiddle via RS232 as (up to five) ascii characters without preceding zeros
                                        ; transmit checksum is calculated
                                        ; w register content is destroyed
                                        ; returns in bank0

                                        ; 65,535        high_byte       low_byte
                                        ;
                                        ; 10,000           39              16
                                        ;  1,000            3             232
                                        ;    100            0             100
                                        ;     10            0              10
                                        ;      1            0               1

                movf    numlow,w        ; get lower byte of number
                bank0                   ;
                movwf   tx_numberl      ; store it for use during calculation
                movf    nummiddle,w     ; get upper byte of number
                movwf   tx_numberh      ; store it for use during calculation
                movlw   b'10000000'     ; use the upper bit of the counter as a flag for preceding zeros,
                movwf   tx_counter      ; 1 means skip characters
                movlw   d'16'           ; first count number of 10,000's, lower byte of binary number 10,000
                movwf   templow         ;
                movlw   d'39'           ; upper byte of binary number 10,000
                movwf   temphigh        ;
                call    TX_LDIGIT       ;
                movlw   d'232'          ; count number of 1,000's, lower byte of binary number 1,000
                movwf   templow         ;
                movlw   d'3'            ; upper byte of binary number 1,000
                movwf   temphigh        ;
                call    TX_LDIGIT       ;
                movlw   d'100'          ; count number of 100's, lower byte of binary number 100
                movwf   templow         ;
                clrf    temphigh        ; high byte is zero
                call    TX_LDIGIT       ;
                movlw   d'10'           ; count number of 10's, lower byte of binary number 10
                movwf   templow         ;
                clrf    temphigh        ; high byte is zero
                call    TX_LDIGIT       ;
                movlw   d'1'            ; count number of 1's, lower byte of binary number 1
                movwf   templow         ;
                clrf    temphigh        ; high byte is zero
                clrf    tx_counter      ; the last character always has to be sent so clear counter and flag bit too
                goto    TX_LDIGIT       ; goto instead of call because we are done here

TX_LDIGIT       movlw   b'10000000'     ; select flag bit
                andwf   tx_counter,f    ; clear counter but keep flag bit for preceding zeros
TX_LTEST        movf    templow,w       ; check if subtraction will give positive or zero result
                subwf   tx_numberl,w    ; compare lower byte
                movf    temphigh,w      ;
                skpc                    ;
                incfsz  temphigh,w      ; 
                subwf   tx_numberh,w    ; high byte
                skpc                    ; is the result >= zero ?
                goto    TX_LSEND        ; no, result is negative, go send character
TX_LSUBTR       bcf     tx_counter,7    ; yes, clear flag bit since we skipped possible zero character, now start sending characters
                incf    tx_counter,f    ; increment counter
                movf    templow,w       ; subtract
                subwf   tx_numberl,f    ; low byte
                movf    temphigh,w      ;
                skpc                    ; 
                incfsz  temphigh,w      ; high byte
                subwf   tx_numberh,f    ;
                goto    TX_LTEST        ; next

TX_LSEND        btfsc   tx_counter,7    ; can we still skip the preceding zero characters ?
                return                  ; yes, skip those characters
                movlw   a'0'            ; in ascii numbers start at 30 hex
                addwf   tx_counter,w    ; so add the counter to get the right ascii code
                call    TX_BYTE         ; send the ascii character stored in register w,return in bank0
                return                  ; return in bank0

;--------------------------------------------------------------------------------------------------------------------------------------

TX_WNUMBER                              ; this routine transmits the 24 bit number (0-16777215)('wide' number) in the registers
                                        ; numlow(lsB), nummiddle (middle byte) and numhigh (msB) (word extended) via RS232 as
                                        ; (up to eight) ascii characters without preceding zeros, a transmit checksum is calculated
                                        ; w register content is destroyed
                                        ; returns in bank0
                                        ;
                                        ; 16,777,215    upper_byte      middle_byte     lower_byte
                                        ; 10,000,000            152            150             128 
                                        ;  1,000,000             15             66              64
                                        ;    100,000              1            134             160
                                        ;     10,000              0             39              16
                                        ;      1,000              0              3             232
                                        ;        100              0              0             100
                                        ;         10              0              0              10
                                        ;          1              0              0               1

                bank0                   ;
                movf    numlow,w        ; get lower byte of number
                movwf   tx_numberl      ; store it for use during calculation
                movf    nummiddle,w     ; get middle byte of number
                movwf   tx_numberm      ; store it for use during calculation
                movf    numhigh,w       ; get upper byte of number
                movwf   tx_numberh      ; store it for use during calculation
                movlw   b'10000000'     ; use the upper bit of the counter as a flag for preceding zeros,
                movwf   tx_counter      ; 1 means skip characters
                movlw   d'128'          ; first count number of 10,000,000's, lower byte
                movwf   templow         ;
                movlw   d'150'          ; middle byte
                movwf   tempmiddle      ;
                movlw   d'152'          ; upper byte
                movwf   temphigh        ;
                call    TX_WDIGIT       ;
                movlw   d'64'           ; count number of 1,000,000's, lower byte
                movwf   templow         ;
                movlw   d'66'           ; middle byte
                movwf   tempmiddle      ;
                movlw   d'15'           ; upper byte
                movwf   temphigh        ;
                call    TX_WDIGIT       ;
                movlw   d'160'          ; count number of 100,000's, lower byte
                movwf   templow         ;
                movlw   d'134'          ; middle byte
                movwf   tempmiddle      ;
                movlw   d'1'            ; upper byte
                movwf   temphigh        ;
                call    TX_WDIGIT       ;
                movlw   d'16'           ; count number of 10,000's, lower byte
                movwf   templow         ;
                movlw   d'39'           ; middle byte
                movwf   tempmiddle      ;
                clrf    temphigh        ; upper byte is zero
                call    TX_WDIGIT       ;
                movlw   d'232'          ; number of 1,000's
                movwf   templow         ;
                movlw   d'3'            ; middle byte
                movwf   tempmiddle      ;
                clrf    temphigh        ; upper byte is zero
                call    TX_WDIGIT       ;
                movlw   d'100'          ; number of 100's
                movwf   templow         ;
                clrf    tempmiddle      ; middle byte is zero
                clrf    temphigh        ; upper byte is zero
                call    TX_WDIGIT       ;
                movlw   d'10'           ; count number of 10's
                movwf   templow         ;
                clrf    tempmiddle      ; middle byte is zero
                clrf    temphigh        ; upper byte is zero
                call    TX_WDIGIT       ;
                movlw   b'00000001'     ; count number of 1's
                movwf   templow         ;
                clrf    tempmiddle      ; middle byte is zero
                clrf    temphigh        ; high byte is zero
                clrf    tx_counter      ; the last character always has to be sent so clear counter and flag bit too
                goto    TX_WDIGIT       ; goto instead of call because we are done here

TX_WDIGIT       movlw   b'10000000'     ; select flag bit
                andwf   tx_counter,f    ; clear counter but keep flag bit for preceding zeros

TX_WTEST        movf    templow,w       ; check if subtraction will give positive or zero result
                subwf   tx_numberl,w    ; compare lower byte
                movf    tempmiddle,w    ;
                skpc                    ;
                incfsz  tempmiddle,w    ; 
                subwf   tx_numberm,w    ; middle byte
                movf    temphigh,w      ;
                skpc                    ; 
                incfsz  temphigh,w      ; high byte
                subwf   tx_numberh,w    ;
                skpc                    ; is the result >= zero ?
                goto    TX_WSEND        ; no, result is negative, go send character
TX_WSUBTR       bcf     tx_counter,7    ; yes, clear flag bit since we skipped possible zero character, now start sending characters
                incf    tx_counter,f    ; increment counter
                movf    templow,w       ; subtract
                subwf   tx_numberl,f    ; low byte
                movf    tempmiddle,w    ;
                skpc                    ;
                incfsz  tempmiddle,w    ; 
                subwf   tx_numberm,f    ; middle byte
                movf    temphigh,w      ;
                skpc                    ; 
                incfsz  temphigh,w      ; high byte
                subwf   tx_numberh,f    ;
                goto    TX_WTEST        ; next

TX_WSEND        btfsc   tx_counter,7    ; can we still skip the preceding zero characters ?
                return                  ; yes, skip those characters
                movlw   a'0'            ; in ascii numbers start at 30 hex
                addwf   tx_counter,w    ; so add the counter to get the right ascii code
                call    TX_BYTE         ; send the ascii character stored in register w,return in bank0
                return                  ; return in bank0

;--------------------------------------------------------------------------------------------------------------------------------------

TX_TIMEANDDATE                          ; this routine transmits the time and date values from the buffer in bank2 as a string
                                        ; use Windows format: 'DD/MM/YY HH:MM:SS'
                                        ; transmit checksum is calculated
                                        ; w register content is destroyed
                                        ; returns in bank2

                bank2                   ;
                swapf   date,w          ; get date/ 10 date
                andlw   b'00000011'     ; strip date
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                bank2                   ;
                movf    date,w          ; get date/ 10 date
                andlw   b'00001111'     ; strip 10 date
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                movlw   a'/'            ;
                call    TX_BYTE         ;
                bank2                   ;
                swapf   month,w         ; get months/ 10 month
                andlw   b'00000001'     ; strip months
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                bank2                   ;
                movf    month,w         ; get months/ 10 month
                andlw   b'00001111'     ; strip 10 month
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                movlw   a'/'            ;
                call    TX_BYTE         ;
                bank2                   ;
                swapf   year,w          ; get year/ 10 year
                andlw   b'00001111'     ; strip year
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                bank2                   ;
                movf    year,w          ; get year/ 10 year
                andlw   b'00001111'     ; strip 10 year
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                movlw   a' '            ;
                call    TX_BYTE         ;
                bank2                   ;
                swapf   hours,w         ; get hours/ 10 hours
                andlw   b'00000011'     ; strip hours
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                bank2                   ;
                movf    hours,w         ; get 10 hours/ hours
                andlw   b'00001111'     ; strip 10 hours
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                movlw   a':'            ;
                call    TX_BYTE         ;
                bank2                   ;
                swapf   minutes,w       ; get minutes/ 10 minutes
                andlw   b'00000111'     ; strip minutes
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                bank2                   ;
                movf    minutes,w       ; get 10 minutes/ minutes
                andlw   b'00001111'     ; strip 10 minutes
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                movlw   a':'            ;
                call    TX_BYTE         ;
                bank2                   ;
                swapf   seconds,w       ; get seconds/ 10 seconds / oscillator enable
                andlw   b'00000111'     ; strip seconds / oscillator enable
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                bank2                   ;
                movf    seconds,w       ; get seconds/ 10 seconds / oscillator enable
                andlw   b'00001111'     ; strip 10 seconds / oscillator enable
                call    TX_BINVALUE     ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9') 
                return                  ; return in bank2


;--------------------------------------------------------------------------------------------------------------------------------------

TX_FREQVALSTR                           ; w register should hold the frequency settings value
                                        ; this routine transmits the corresponding value from the table below as an ascii string 

                                        ; 00000000      32
                                        ; 00000001      16
                                        ; 00000011      8
                                        ; 00000111      4
                                        ; 00001111      2
                                        ; 00011111      1
                                        ; 00111111      0.5
                                        ; 01111111      0.25
                                        ; 11111111      0

TX_FREQ_32      bank0                   ;
                movwf   tx_numberl      ;
                movlw   b'00000000'     ;
                subwf   tx_numberl,w    ;
                skpz                    ;
                goto    TX_FREQ_16      ;
                movlw   a'3'            ;
                call    TX_BYTE         ; transmit byte , return in bank0
                movlw   a'2'            ;
                call    TX_BYTE         ; transmit byte , return in bank0
                return                  ; return in bank0

TX_FREQ_16      movlw   b'00000001'     ;
                subwf   tx_numberl,w    ;
                skpz                    ;
                goto    TX_FREQ_8       ;
                movlw   a'1'            ;
                call    TX_BYTE         ;
                movlw   a'6'            ;
                call    TX_BYTE         ;
                return                  ;

TX_FREQ_8       movlw   b'00000011'     ;
                subwf   tx_numberl,w    ;
                skpz                    ;
                goto    TX_FREQ_4       ;
                movlw   a'8'            ;
                call    TX_BYTE         ;
                return                  ;

TX_FREQ_4       movlw   b'00000111'     ;
                subwf   tx_numberl,w    ;
                skpz                    ;
                goto    TX_FREQ_2       ;
                movlw   a'4'            ;
                call    TX_BYTE         ;
                return                  ;

TX_FREQ_2       movlw   b'00001111'     ;
                subwf   tx_numberl,w    ;
                skpz                    ;
                goto    TX_FREQ_1       ;
                movlw   a'2'            ;
                call    TX_BYTE         ;
                return                  ;

TX_FREQ_1       movlw   b'00011111'     ;
                subwf   tx_numberl,w    ;
                skpz                    ;
                goto    TX_FREQ_0.5     ;
                movlw   a'1'            ;
                call    TX_BYTE         ;
                return                  ;

TX_FREQ_0.5     movlw   b'00111111'     ;
                subwf   tx_numberl,w    ;
                skpz                    ;
                goto    TX_FREQ_0.25    ;
                movlw   a'0'            ;
                call    TX_BYTE         ;
                movlw   a'.'            ;
                call    TX_BYTE         ;
                movlw   a'5'            ;
                call    TX_BYTE         ;
                return                  ;

TX_FREQ_0.25    movlw   b'01111111'     ;
                subwf   tx_numberl,w    ;
                skpz                    ;
                goto    TX_FREQ_0       ;
                movlw   a'0'            ;
                call    TX_BYTE         ;
                movlw   a'.'            ;
                call    TX_BYTE         ;
                movlw   a'2'            ;
                call    TX_BYTE         ;
                movlw   a'5'            ;
                call    TX_BYTE         ;
                return                  ;

TX_FREQ_0       movlw   b'11111111'     ;
                subwf   tx_numberl,w    ;
                skpz                    ;
                return                  ;
                movlw   a'0'            ;
                call    TX_BYTE         ;
                return                  ;


;--------------------------------------------------------------------------------------------------------------------------------------

GET_NUMBER                              ; reads the ascii characters of the RS232 input buffer and converts this data to an
                                        ; eight bit number (0-255), preceding zeros are accepted
                                        ; returns in bank 0 with:
                                        ; w holds number - carry is cleared - read OK
                                        ; w holds junk   - carry is set     - error: not a number

                bank0                   ;
                clrf    templow         ; we will add the values of each character to this register, so start with zero
                movlw   (rx_buffer00+2) ; start position of data in buffer (runs up to and includes maxpos)
                movwf   fsr             ; point to this address
                bcf     status,irp      ; make sure upper bit in address is zero (select bank 0 and 1)
READVALUE       movlw   h'3A'           ; subtract 3A hex from the buffer number
                subwf   indf,w          ; if the character is a number, the result should be negative
                skpnc                   ; is the result negative?
HIGHERR         return                  ; no, so it is not an ascii number, we cannot handle this data, exit with carry set, error
                addlw   h'0A'           ; yes, if the character is a number then the result of this addition should be positive
                skpc                    ; is the result positive ?
LOWERR          goto    GN_ERROR        ; no, we cannot handle this data
ADDVALUE        addwf   templow,f       ; yes, store the resulting value
                skpnc                   ; is the resulting number greater than 255 decimal ?
                return                  ; yes, so it won't fit in 1 byte, we cannot handle this data, exit with carry set, error
POINTER         incf    fsr,f           ; no, increase pointer to point to next value
                movf    fsr,w           ; use pointer value
                subwf   rx_maxpos,w     ; was this the last character ?
                skpc                    ; let's see
                goto    READOK          ; yes, end loop and go to exit with carry cleared as indication for no errors
TESTSIZE        movlw   d'26'           ; the value should be smaller than 26 because 25 times 10 will be 250
                subwf   templow,w       ; and we know there is a next character, result should fit in one byte
                skpnc                   ; will the result times 10 be larger than 250 ?
                return                  ; yes, we cannot handle this data, exit with carry set, error
TIMES10         clrc                    ; no, go multiply the current value by ten, start off with clear carry bit:
                rlf     templow,f       ; first times two,
                movf    templow,w       ; store in w,
                rlf     templow,f       ; and this makes times 4,
                rlf     templow,f       ; and this times 8,
                addwf   templow,f       ; plus 2 is 10 times
                goto    READVALUE       ; next, do loop again and add new numbers

GN_ERROR        setc                    ; indication for not a number, error 
                return                  ; done

READOK          movf    templow,w       ; what is the resulting number value of the ascii characters ?
                return                  ; done, exit with carry cleared as indication for no errors



;--------------------------------------------------------------------------------------------------------------------------------------

GET_LNUMBER                             ; reads the ascii characters of the RS232 input buffer and converts this data to an
                                        ; a number from 0-65535, preceding zeros are accepted
                                        ; returns in bank 0 with:
                                        ; numlow & nummiddle hold number - carry is cleared - read OK
                                        ; numlow & nummiddle hold junk   - carry is set     - error: not a number

                bank0                   ;
                clrf    numlow          ; we will add the values of each character to these registers,
                clrf    nummiddle       ; so start with zero
                movlw   (rx_buffer00+2) ; start position of data in buffer (runs up to and includes maxpos)
                movwf   fsr             ; point to this address
                bcf     status,irp      ; make sure upper bit in address is zero (select bank 0 and 1)
LREADVALUE      movlw   h'3A'           ; subtract 3A hex from the buffer number
                subwf   indf,w          ; if the character is a number, the result should be negative
                skpnc                   ; is the result negative?
LHIGHERR        goto    LGN_ERROR       ; no, so it is not an ascii number we cannot handle this data
                addlw   h'0A'           ; yes, if the character is a number then the result of this addition should be positive
                skpc                    ; is the result positive ?
LLOWERR         goto    LGN_ERROR       ; no, we cannot handle this data
LADDVALUE       addwf   numlow,f        ; yes, store the resulting value
                skpc                    ; is the resulting number greater than 255 decimal ?
                goto    LPOINTER        ; no, go get next character
                incf    nummiddle,f     ; yes, add one count to upper byte of number (perform two byte add with carry)
                skpnz                   ; is the resulting number greater than 65535 decimal ?
                goto    LGN_ERROR       ; yes, so it won't fit in 2 bytes, we cannot handle this data
LPOINTER        incf    fsr,f           ; no, increase pointer to point to next value
                movf    fsr,w           ; use pointer value
                subwf   rx_maxpos,w     ; was this the last character ?
                skpc                    ; let's see
                return                  ; yes, end loop and exit with carry cleared as indication for no errors
LTESTSIZE       movlw   d'231'          ; the value of the upper byte should not be more than 25 because 25 times 10 will be 250
                addwf   nummiddle,w     ; and we know there is a next character, the result should fit in two bytes
                skpnc                   ; is the value of the upper byte equal or more than 25 ?                                
                goto    LTESTMORE       ; yes, go see how much it is exactly and see how much is the lower byte
LTIMES10        bcf     status,c        ; yes, go multiply the current value by ten, start off with clear carry bit:
                rlf     numlow,f        ; first times two,
                movf    numlow,w        ;
                movwf   templow         ; store value temporarily
                rlf     nummiddle,f     ; first times two, use carry bit from lower byte
                movf    nummiddle,w     ;
                movwf   temphigh        ; store value temporarily
                rlf     numlow,f        ; and this makes times 4, lower byte
                rlf     nummiddle,f     ; and this makes times 4, upper byte, use carry bit of lower byte
                rlf     numlow,f        ; and this makes times 8, lower byte
                rlf     nummiddle,f     ; and this makes times 8, upper byte, use carry bit of lower byte
                movf    templow,w       ;
                addwf   numlow,f        ; plus 2 is 10 times, lower byte
                skpnc                   ;
                incf    nummiddle,f     ; transport carry bit
                movf    temphigh,w      ;
                addwf   nummiddle,f     ; plus 2 is 10 times, upper byte
                goto    LREADVALUE      ; next, do loop again and add new numbers

LTESTMORE       skpz                    ; is the value exactly 25 ?
                return                  ; no, number won't fit in two bytes therefore we cannot handle this data, exit with carry set
                movlw   d'154'          ; yes, lower byte should be smaller than 154 because the total value should be less than 6553
                subwf   numlow,w        ;
                skpc                    ; is the number represented by the two bytes smaller than 6554 ?
                goto    LTIMES10        ; yes, continue and multiply by 10 and then fetch next character
LGN_ERROR       setc                    ; set error flag
                return                  ; no, number won't fit in two bytes therefore we cannot handle this data, exit with carry set

;--------------------------------------------------------------------------------------------------------------------------------------

GET_TIMEANDDATE                         ; convert the 17 byte Windows format input string 'DD/MM/YY HH:MM:SS' into eight bytes for
                                        ; clock, store the values in the time and date buffer in bank2
                                        ; w register content destroyed
                                        ; returns in bank2


                bank0                   ; DATE
                movlw   h'30'           ; ascii to binary offset
                subwf   rx_buffer03,w   ; get Day and convert from ascii character to binary value
                bank2                   ;
                movwf   date            ; move value to clock buffer
                bank0                   ;
                movlw   h'30'           ;
                subwf   rx_buffer02,f   ; get 10 Days and convert from ascii character to binary value
                swapf   rx_buffer02,w   ; place result in upper four bits
                bank2                   ;
                iorwf   date,f          ;
                bank0                   ; MONTH
                movlw   h'30'           ; ascii to binary offset
                subwf   rx_buffer06,w   ; get Months and convert from ascii character to binary value
                bank2                   ;
                movwf   month           ; move value to clock buffer
                bank0                   ;
                movlw   h'30'           ;
                subwf   rx_buffer05,f   ; get 10 Months and convert from ascii character to binary value
                swapf   rx_buffer05,w   ; place result in upper four bits
                bank2                   ;
                iorwf   month,f         ;
                bank0                   ; YEAR
                movlw   h'30'           ; ascii to binary offset
                subwf   rx_buffer09,w   ; get Years and convert from ascii character to binary value
                bank2                   ;
                movwf   year            ; move value to clock buffer
                bank0                   ;
                movlw   h'30'           ;
                subwf   rx_buffer08,f   ; get 10 Years and convert from ascii character to binary value
                swapf   rx_buffer08,w   ; place result in upper four bits
                bank2                   ;
                iorwf   year,f          ;
                bank0                   ; HOURS
                movlw   h'30'           ; ascii to binary offset
                subwf   rx_buffer12,w   ; get Hours and convert from ascii character to binary value
                bank2                   ;
                movwf   hours           ; move value to clock buffer
                bank0                   ;
                movlw   h'30'           ;
                subwf   rx_buffer11,f   ; get 10 Hours and convert from ascii character to binary value
                swapf   rx_buffer11,w   ; place result in upper four bits
                bank2                   ;
                iorwf   hours,f         ;
                bcf     hours,6         ; select 24 hour clock
                bank0                   ; MINUTES
                movlw   h'30'           ; ascii to binary offset
                subwf   rx_buffer15,w   ; get Minutes and convert from ascii character to binary value
                bank2                   ;
                movwf   minutes         ; move value to clock buffer
                bank0                   ;
                movlw   h'30'           ;
                subwf   rx_buffer14,f   ; get 10 Minutes and convert from ascii character to binary value
                swapf   rx_buffer14,w   ; place result in upper four bits
                bank2                   ;
                iorwf   minutes,f       ;
                bank0                   ; SECONDS
                movlw   h'30'           ; ascii to binary offset
                subwf   rx_buffer18,w   ; get Seconds and convert from ascii character to binary value
                bank2                   ;
                movwf   seconds         ; move value to clock buffer
                bank0                   ;
                movlw   h'30'           ;
                subwf   rx_buffer17,f   ; get 10 Seconds and convert from ascii character to binary value
                swapf   rx_buffer17,w   ; place result in upper four bits
                bank2                   ;
                iorwf   seconds,f       ;
                bcf     seconds,7       ; set bit to enable clock oscillator, is CH (clock halt) bit in clock chip 
                movlw   d'1'            ; day 1
                movwf   day             ; just start DAY start at day 1, Sunday, although we don't know this for sure, doesn't matter
                movlw   b'10000011'     ; set square wave at 32768 Hz but disabled and it's output high 
                movwf   clockctrl       ; CONTROL
                return                  ; return in bank2


;--------------------------------------------------------------------------------------------------------------------------------------

GET_FREQVALNUM                          ; convert string data that goes with the logfrequency setting commands to the right lograte
                                        ; value according to the following table (example for 16 Hz: 16 > 00000001) 
                                        ; carry cleared - value is ok
                                        ; carry set - error reading number

                                        ; 32            00000000
                                        ; 16            00000001
                                        ; 8             00000011
                                        ; 4             00000111
                                        ; 2             00001111
                                        ; 1             00011111
                                        ; 0.5           00111111
                                        ; 0.25          01111111
                                        ; 0             11111111  (actually 1xxxxxxx is used since only bit 7 is tested)

F_32            bank0                   ;
                movlw   a'3'            ; test first data byte
                subwf   rx_buffer02,w   ;
                skpz                    ;
                goto    F_16            ;
                movlw   a'2'            ;
                subwf   rx_buffer03,w   ; test second data byte
                skpz                    ;
                goto    F_ERROR         ;
                movlw   rx_buffer03     ;
                subwf   rx_maxpos,w     ; test length
                skpz                    ;
                goto    F_ERROR         ;
                movlw   b'00000000'     ;
                clrc                    ; clear carry to indicate result is ok
                return

F_16            movlw   a'1'            ; test first data byte
                subwf   rx_buffer02,w   ;
                skpz                    ;
                goto    F_8             ;
                movlw   rx_buffer03     ;
                subwf   rx_maxpos,w     ; test length first, we not only have the value 16 but also the value 1
                skpz                    ;
                goto    F_1             ;
                movlw   a'6'            ;
                subwf   rx_buffer03,w   ; test second data byte
                skpz                    ;
                goto    F_ERROR         ;
                movlw   b'00000001'     ;
                clrc                    ; clear carry to indicate result is ok
                return

F_8             movlw   a'8'            ; test first data byte
                subwf   rx_buffer02,w   ;
                skpz                    ;
                goto    F_4             ;
                movlw   rx_buffer02     ;
                subwf   rx_maxpos,w     ; test length
                skpz                    ;
                goto    F_ERROR         ;
                movlw   b'00000011'     ;
                clrc                    ; clear carry to indicate result is ok
                return

F_4             movlw   a'4'            ; test first data byte
                subwf   rx_buffer02,w   ;
                skpz                    ;
                goto    F_2             ;
                movlw   rx_buffer02     ;
                subwf   rx_maxpos,w     ; test length
                skpz                    ;
                goto    F_ERROR         ;
                movlw   b'00000111'     ;
                clrc                    ; clear carry to indicate result is ok
                return

F_2             movlw   a'2'            ; test first data byte
                subwf   rx_buffer02,w   ;
                skpz                    ;
                goto    F_1             ;
                movlw   rx_buffer02     ;
                subwf   rx_maxpos,w     ; test length
                skpz                    ;
                goto    F_ERROR         ;
                movlw   b'00001111'     ;
                clrc                    ; 
                return                  ; clear carry to indicate result is ok

F_1             movlw   a'1'            ; test first data byte
                subwf   rx_buffer02,w   ;
                skpz                    ;
                goto    F_0             ;
                movlw   rx_buffer02     ;
                subwf   rx_maxpos,w     ; test length
                skpz                    ;
                goto    F_ERROR         ;
                movlw   b'00011111'     ;
                clrc                    ; clear carry to indicate result is ok
                return

F_0             movlw   a'0'            ; test first data byte
                subwf   rx_buffer02,w   ;
                skpz                    ;
                goto    F_ERROR         ;
                movlw   rx_buffer02     ;
                subwf   rx_maxpos,w     ; test length
                skpz                    ;
                goto    F_0.5           ;
                movlw   b'11111111'     ;
                clrc                    ; clear carry to indicate result is ok
                return

F_0.5           movlw   a'.'            ; test second data byte
                subwf   rx_buffer03,w   ;
                skpz                    ;
                goto    F_ERROR         ;
                movlw   a'5'            ; test third data byte
                subwf   rx_buffer04,w   ;
                skpz                    ;
                goto    F_0.25          ;
                movlw   rx_buffer04     ;
                subwf   rx_maxpos,w     ; test length
                skpz                    ;
                goto    F_ERROR         ;
                movlw   b'00111111'     ;
                clrc                    ; clear carry to indicate result is ok
                return

F_0.25          movlw   a'2'            ; test third data byte
                subwf   rx_buffer04,w   ;
                skpz                    ;
                goto    F_ERROR         ;
                movlw   a'5'            ; test fourth data byte
                subwf   rx_buffer05,w   ;
                skpz                    ;
                goto    F_ERROR         ;
                movlw   rx_buffer05     ;
                subwf   rx_maxpos,w     ; test length
                skpz                    ;
                goto    F_ERROR         ;
                movlw   b'01111111'     ;
                clrc                    ; clear carry to indicate result is ok
                return

F_ERROR         setc                    ; none of the values matched
                return                  ; return with carry set as indication result is not ok


;--------------------------------------------------------------------------------------------------------------------------------------
; Commands:
;--------------------------------------------------------------------------------------------------------------------------------------

COMMANDS                                ; label to allow use of pagesel

;--------------------------------------------------------------------------------------------------------------------------------------


EXIT_ACK        movlw   h'06'           ; acknowledgement (ACK)
                call    TX_BYTE         ; send 'command has been accepted', return in bank0
                bcf     flags1,command  ; now command has been handled we can clear the execute flag, ready for next command
                bcf     portc,cts_out   ; set CTS to re-enable data stream from computer
                bsf     portb,led_red   ; turn on red status led
                bcf     portb,led_green ; turn off green status led
                return                  ; done

;--------------------------------------------------------------------------------------------------------------------------------------

EXIT_NAK        movlw   h'15'           ; unknown command, exit, send negative acknowledgement (NAK)
                call    TX_BYTE         ; send 'command has NOT been accepted', return in bank0
                bcf     flags1,command  ; clear any command bytes received up to now get, ready for next command
                bcf     portc,cts_out   ; set CTS to re-enable data stream from computer
                bsf     portb,led_red   ; turn on red status led
                bcf     portb,led_green ; turn off green status led
                return                  ; go back to main program loop see what else we can do

;--------------------------------------------------------------------------------------------------------------------------------------

AU                                      ; read the string with the name of the author of this software uses a table read that works
                                        ; anywhere in the memory. A normal table read using just the 'addwf pcl' instruction will only
                                        ; work if the program code and table code are entirely in the first 8-bit page of program
                                        ; memory = the first 256 memory locations !
                                        ; a write opens the backdoor for the serial number write (command SE)

                btfsc   flags1,withdata ; is there any data ?
                goto    AU_COMMAND      ; yes, so we should use this data
                call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                bank3                   ;
                clrf    tablepointer    ; start the reading of the table at the first position
AU_LOOP         movlw   high AU_TABLE   ; see where the assembler will put the table in PIC memory and get the high byte
                movwf   pclath          ; now move this into pclath
                movlw   low AU_TABLE+1  ; load w with the memory address low byte of the first piece of data in the table
                addwf   tablepointer,w  ; calculate the offset address and see if it overflows
                skpnc                   ; did it overflow ?
                incf    pclath,f        ; yes, so increase pclath one count
AU_GET_CHAR     call    AU_TABLE        ; no, lookup value in table, returns ascii character in w register
                xorlw   d'00'           ; zero indicates end of table
                skpnz                   ; are we at the end of the table ?
                goto    TX_COMMANDEND   ; yes, exit: send checksum and ETX, clear command flag
                call    TX_BYTE         ; returns in bank0
                bank3                   ;
                incf    tablepointer,f  ; point to next position in table
                goto    AU_LOOP         ;

AU_TABLE        movwf   pcl             ; w contains low program counter byte and points to the next location plus counter
                retlw   a'A'            ; offset text in table is 'Andries C. Tip'
                retlw   a'n'            ;
                retlw   a'd'            ; Note: in a Microchip application note it states that it is neccesary to disable the
                retlw   a'r'            ; interrupts before executing the instruction movwf pcl because an interrupt during
                retlw   a'i'            ; this instruction may cause the program to jump to an unknown address. It turnes
                retlw   a'e'            ; out to be not true, so table reads do not have to disable any interrupts.
                retlw   a's'            ;
                retlw   a' '            ;
                retlw   a'C'            ;
                retlw   a'.'            ;
                retlw   a' '            ;
                retlw   a'T'            ;
                retlw   a'i'            ;
                retlw   a'p'            ;
                retlw   d'00'           ; end of message

AU_COMMAND      bsf     flags1,changese ; backdoor to allow serial number to be changed
                goto    EXIT_NAK        ; exit and be ready for next command

;--------------------------------------------------------------------------------------------------------------------------------------

CA                                      ; read/write value of static ram address (value 0..63)
                                        ; write: input is ascii string with 8 bit number

                btfsc   flags1,withdata ; is there any data ?
                goto    CA_COMMAND      ; yes, so we should use this data
CA_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                bank2                   ;
                movf    clockaddr,w     ;
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

CA_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                andlw   b'11000000'     ; the maximum address for the clock chip static ram and control bytes is 63
                skpz                    ; is the address value valid ?
                goto    EXIT_NAK        ; no, exit and be ready for next command
                call    GET_NUMBER      ; again, read the data input number that goes with the command into the w register
                bank2                   ;
                movwf   clockaddr       ;
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

CD                                      ; IIC clock chip serial control bytes and static ram read/ write

                btfsc   flags1,withdata ; is there any data ?
                goto    CD_COMMAND      ; yes, so we should use this data
CD_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                pagesel IIC_RD_CLKCHIP  ; make right program memory page selection
                call    IIC_RD_CLKCHIP  ; get data byte from clock chip into w register
                pagesel TX_NUMBER       ; make right program memory page selection
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

CD_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                pagesel IIC_WR_CLKCHIP  ; make right program memory page selection
                call    IIC_WR_CLKCHIP  ; write byte to static ram
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

CL                                      ; read/set clock time and data in the following format HH:MM:SS MM/DD/YY
                                        ; external clock chip is Dallas DS1307 (www.dalsemi.com)        

                btfsc   flags1,withdata ; is there any data ?
                goto    CL_COMMAND      ; yes, so we should use this data to write to the clock chip through IIC
CL_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                pagesel IIC_RDCLOCK     ; make right program memory page selection
                call    IIC_RDCLOCK     ; read the eight time related bytes from external clock chip into buffer (bank2, 68..6F hex)
                pagesel TX_TIMEANDDATE  ; make right program memory page selection
                call    TX_TIMEANDDATE  ; send time and date values from the buffer in bank2 as a string, bank2 return
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag
CL_COMMAND      call    GET_TIMEANDDATE ; convert string serial input buffer, store in time and date buffer in bank2, bank2 return
                pagesel IIC_WRCLOCK     ; make right program memory page selection
                call    IIC_WRCLOCK     ; write the eight time related bytes from buffer in bank2 (68..6F) to clock
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

EA                                      ; set eeprom read/write address
                                        ; write: input string is 8 bit address

                btfsc   flags1,withdata ; is there any data ?
                goto    EA_COMMAND      ; yes, so we should use this data to write to the eeprom location
EA_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                bank2                   ;
                movf    iee_address,w   ; get address of eeprom location
                bank0                   ;
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

EA_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                bank2                   ;
                movwf   iee_address     ; store the number for later use with command ED
                bank0                   ;
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

ED                                      ; read/write eeprom memory location
                                        ; write: input is ascii string with 8 bit number

                btfsc   flags1,withdata ; is there any data ?
                goto    ED_COMMAND      ; yes, so we should use this data to write to the eeprom location
ED_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                bank2                   ;
                movf    iee_address,w   ; get address of eeprom location
                bank0                   ;
                pagesel IEE_READ        ; make right program memory page selection
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                pagesel TX_NUMBER       ; make right program memory page selection
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

ED_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                pagesel IEE_WRITE       ; no, make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

ER                                      ; read/write error flags byte
                                        ; write: input is ascii string with 8 bit number

                btfsc   flags1,withdata ; is there any data ?
                goto    ER_COMMAND      ; yes, so we should use this data to write to the eeprom location
ER_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                bank3                   ;
                movf    errors,w        ; get value of error flags register
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

ER_COMMAND      movlw   d'15'           ; eeprom address for error flags register
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                movwf   errors          ; write value to error flags register
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FA                                      ; read/write air temperature logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FA_COMMAND      ; yes, so we should use this data to write to the eeprom location
FA_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_air,w      ; get value of lograte for air temperature channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FA_COMMAND      movlw   d'21'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_air        ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FB                                      ; read/write brake switch logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FB_COMMAND      ; yes, so we should use this data to write to the eeprom location
FB_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_brake,w    ; get value of lograte for brake switch channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FB_COMMAND      movlw   d'27'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_brake      ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FE                                      ; read/write lateral acceleration logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FE_COMMAND      ; yes, so we should use this data to write to the eeprom location
FE_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_lat,w      ; get value of lograte for lateral acceleration channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FE_COMMAND      movlw   d'25'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_lat        ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FH                                      ; read/write thermocouple logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FH_COMMAND      ; yes, so we should use this data to write to the eeprom location
FH_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_tc,w       ; get value of lograte for thermocouple channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FH_COMMAND      movlw   d'20'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_tc         ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FL                                      ; read/write lambda logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FL_COMMAND      ; yes, so we should use this data to write to the eeprom location
FL_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_lambda,w   ; get value of lograte for lambda channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FL_COMMAND      movlw   d'18'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_lambda     ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FM                                      ; read/write mark switch logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FM_COMMAND      ; yes, so we should use this data to write to the eeprom location
FM_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_mark,w     ; get value of lograte for mark switch channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FM_COMMAND      movlw   d'26'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_mark       ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FO                                      ; read/write longitudinal acceleration logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FO_COMMAND      ; yes, so we should use this data to write to the eeprom location
FO_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_long,w     ; get value of lograte for longitudinal acceleration channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FO_COMMAND      movlw   d'24'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_long       ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FR                                      ; read/write rpm logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FR_COMMAND      ; yes, so we should use this data to write to the eeprom location
FR_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_rpm,w      ; get value of lograte for rpm channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FR_COMMAND      movlw   d'16'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_rpm        ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FS                                      ; read/write speed logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FS_COMMAND      ; yes, so we should use this data to write to the eeprom location
FS_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_speed,w    ; get value of lograte for speed channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FS_COMMAND      movlw   d'17'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_speed      ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FT                                      ; read/write throttle logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FT_COMMAND      ; yes, so we should use this data to write to the eeprom location
FT_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_throttle,w ; get value of lograte for throttle channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FT_COMMAND      movlw   d'23'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_throttle   ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FV                                      ; read/write voltage logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FV_COMMAND      ; yes, so we should use this data to write to the eeprom location
FV_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_voltage,w  ; get value of lograte for voltage channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FV_COMMAND      movlw   d'19'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_voltage    ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

FW                                      ; read/write water temperature logging frequency from/to pic eeprom

                btfsc   flags1,withdata ; is there any data ?
                goto    FW_COMMAND      ; yes, so we should use this data to write to the eeprom location
FW_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                movf    freq_water,w    ; get value of lograte for water temperature channel
                call    TX_FREQVALSTR   ; transmit value as string, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

FW_COMMAND      movlw   d'22'           ; eeprom address for log frequency setting
                bank2                   ;
                movwf   iee_address     ; set internal eeprom address pointer
                call    GET_FREQVALNUM  ; read the value that goes with the command, convert it and put it in w register
                skpnc                   ; were there any errors reading the value ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no,
                movwf   freq_water      ; copy value
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

ID                                      ; read the identification string explaining the application of this PIC chip
                                        ; uses a table read that works anywhere in the memory.
                                        ; A normal table read using just the 'addwf pcl' instruction will only work if the
                                        ; program code and table code are entirely in the first 8-bit page of program
                                        ; memory = the first 256 memory locations !

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
                call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank3                   ;
                clrf    tablepointer    ; start the reading of the table at the first position
ID_LOOP         movlw   high ID_TABLE   ; see where the assembler will put the table in PIC memory and get the high byte
                movwf   pclath          ; now move this into pclath
                movlw   low ID_TABLE+1  ; load w with the memory address low byte of the first piece of data in the table
                addwf   tablepointer,w  ; calculate the offset address and see if it overflows
                skpnc                   ; did it overflow ?
                incf    pclath,f        ; yes, so increase pclath one count
ID_GET_CHAR     call    ID_TABLE        ; no, lookup value in table, returns ascii character in w register
                xorlw   d'00'           ; zero indicates end of table
                skpnz                   ; are we at the end of the table ?
                goto    TX_COMMANDEND   ; yes, exit: send checksum and ETX, clear command status byte
                call    TX_BYTE         ; returns in bank0
                bank3                   ;
                incf    tablepointer,f  ; point to next position in table
                goto    ID_LOOP         ;

ID_TABLE        movwf   pcl             ; w contains low program counter byte and points to the next location plus counter
                retlw   a'M'            ; offset text in table is 'Motorcycle Datalogger'
                retlw   a'o'            ;
                retlw   a't'            ; Note: in a Microchip application note it states that it is neccesary to disable the
                retlw   a'o'            ; interrupts before executing the instruction movwf pcl because an interrupt during
                retlw   a'r'            ; this instruction may cause the program to jump to an unknown address. It turnes
                retlw   a'c'            ; out to be not true, so table reads do not have to disable any interrupts.
                retlw   a'y'            ;
                retlw   a'c'            ;
                retlw   a'l'            ;
                retlw   a'e'            ;
                retlw   a' '            ;
                retlw   a'D'            ;
                retlw   a'a'            ;
                retlw   a't'            ;
                retlw   a'a'            ;
                retlw   a'l'            ;
                retlw   a'o'            ;
                retlw   a'g'            ;
                retlw   a'g'            ;
                retlw   a'e'            ;
                retlw   a'r'            ;
                retlw   d'00'           ; end of message

;--------------------------------------------------------------------------------------------------------------------------------------

KC                                      ; read/write wheel circumference [mm]  to/from pic eeprom
                                        ; value is used in speed calculation
                                        ; write: input is ascii string with 16 bit number

                btfsc   flags1,withdata ; is there any data ?
                goto    KC_COMMAND      ; yes, so we should use this data
KC_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                movlw   d'0'            ; eeprom address 0
                bank2                   ;
                movwf   iee_address     ; set address of pic eeprom location
                pagesel IEE_READ        ; make right program memory page selection
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                movwf   numlow          ;
                movlw   d'1'            ; eeprom address 1
                movwf   iee_address     ; set address of pic eeprom location
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                movwf   nummiddle       ;
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmits the value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte


KC_COMMAND      call    GET_LNUMBER     ; read the data input number that goes with the command into the numlow and nummiddle register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                movlw   d'0'            ;
                bank2                   ;
                movwf   iee_address     ; set address of pic eeprom location
                movf    numlow,w        ;
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                movlw   d'1'            ;
                bank2                   ;
                movwf   iee_address     ; set address of pic eeprom location
                movf    nummiddle,w     ;
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel COPY_WHEELC     ; make right program memory page selection
                call    COPY_WHEELC     ; *** do not update while logging/calculate constant from value 
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

KP                                      ; read/write pulses per revolution to/from pic eeprom
                                        ; value is used in rpm calculation
                                        ; write: input is ascii string with 8 bit number (only values 1 and 2 are allowed)

                btfsc   flags1,withdata ; is there any data ?
                goto    KP_COMMAND      ; yes, so we should use this data
KP_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                movlw   d'4'            ; eeprom address 4
                bank2                   ;
                movwf   iee_address     ; set address of pic eeprom location
                pagesel IEE_READ        ; make right program memory page selection
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                pagesel TX_NUMBER       ; make right program memory page selection
                call    TX_NUMBER       ; transmits the value of w register as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte


KP_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                sublw   d'1'            ; no, the number should be one or two
KP_TESTONE      skpnz                   ; is the value one ?
                goto    KP_VALUEOK      ; yes, go store value
                call    GET_NUMBER      ; no, read the data input number that goes with the command into the w register
                sublw   d'2'            ; the number should be one or two
KP_TESTTWO      skpz                    ; is the value two ?
                goto    EXIT_NAK        ; no, exit and be ready for next command
KP_VALUEOK      movlw   d'4'            ; yes, eeprom address 4
                bank2                   ;
                movwf   iee_address     ; set address of pic eeprom location
                call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel COPY_RPMVALUES  ; make right program memory page selection
                call    COPY_RPMVALUES  ; copy the value from eeprom to pic registers for use during the ccp interrupt 
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

KR                                      ; set or read the maximum RPM rate, above this RPM rate, the shift light will be
                                        ; turned on when the measured rpm value is more than this maximum, otherwise the shift
                                        ; light is off  
                                        ; write: input is ascii string with a number of not more than 14 bits (0..16383)

                btfsc   flags1,withdata ; is there any data ?
                goto    KR_COMMAND      ; yes, so we should use this data
KR_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                movlw   d'2'            ; eeprom address for rpm maximum low byte
                pagesel IEE_READ        ; make right program memory page selection
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                movwf   numlow          ;
                movlw   d'3'            ; eeprom address for rpm maximum high byte
                call    IEE_READ        ; read value from internal eeprom, address in w, data returned in w, return in bank2
                movwf   nummiddle       ;
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmits the value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte


KR_COMMAND      call    GET_LNUMBER     ; read the data input number that goes with the command into the numlow and nummiddle register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                movlw   b'11000000'     ; value for test
                andwf   nummiddle,w     ; test if the upper two bits of the received number are ones
                skpz                    ; is the received number larger than 16383 ?
                goto    EXIT_NAK        ; yes, so it will not fit in 14 bits, exit and be ready for next command
                movlw   d'2'            ; no, continue, this is the eeprom address for the rpm maximum low byte
                bank2                   ;
                movwf   iee_address     ; select eeprom write address
                movf    numlow,w        ;
                pagesel IEE_WRITE       ; make right program memory page selection
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                movlw   d'3'            ; eeprom address for the rpm maximum high byte
                bank2                   ;
                movwf   iee_address     ; select eeprom write address
                movf    nummiddle,w     ;
                call    IEE_WRITE       ; write data to eeprom, address in iee_address, data in w
                pagesel COPY_RPMVALUES  ; make right program memory page selection
                call    COPY_RPMVALUES  ; copy the value from eeprom to pic registers for use during the ccp interrupt 
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

LS                                      ; **** select start condition for logging (RPM, mark button...)
                                        ; input value is eight bit number

                btfsc   flags1,withdata ; is there any data ?
                goto    LS_COMMAND      ; yes, so we should use this data
LS_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'0'            ; **** dummy value 
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

LS_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                nop                     ; **** store the value in the proper place
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

LV                                      ; **** select value for start condition for logging (when RPM, Speed...)
                                        ; input value is 16 bit, use 14 bits for RPM or 8 bits for speed 

                btfsc   flags1,withdata ; is there any data ?
                goto    LV_COMMAND      ; yes, so we should use this data
LV_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'0'            ; **** dummy value
                movwf   numlow          ;
                movwf   nummiddle       ;
                call    TX_LNUMBER      ; transmits the value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

LV_COMMAND      call    GET_LNUMBER     ; read the data input number that goes with the command into numlow and nummiddle registers
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                ;nop                    ; **** store the value in the proper place
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

RC                                      ; clear all record memory 

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
RC_COMMAND                              ; empty line to prevent illegal label error from MPLAB IDE
                pagesel MEM_CLEAR       ; make right program memory page selection
                call    MEM_CLEAR       ; clear all bytes of the table of contents, return in bank3
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

RD                                      ; download record data in blocks of 64 bytes

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
RD_REQUEST      bank3                   ; check if there are any records
                movf    current_rec,w   ; get currently selected record number, zero for no selection yet or when no records available
                skpnz                   ; are there any records and has there been a record selected yet ?
                goto    EXIT_NAK        ; no, exit
                pagesel MEM_RDBLOCK     ; yes, make right program memory page selection
                call    MEM_RDBLOCK     ; copy data block to buffer in bank1, increase blocknumber, carry set when all blocks were done
                pagesel EXIT_NAK        ; make right program memory page selection
RD_RI_CODE      skpnc                   ; have we sent all blocks ? (from here same code for RD and RI commands)
                goto    EXIT_NAK        ; yes, exit
RD_TRANSMIT     call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                bcf     status,irp      ; make sure upper bit in address is zero (select bank 0 and 1)
                movlw   blockbuff00     ; start position of data in buffer in bank1
                movwf   fsr             ; point to this address
RD_LOOP         bank1                   ; the buffer is in bank 1
                movf    indf,w          ; read the data byte at the right position in the buffer
                call    TX_BYTE         ; transmit the value of the w register, return in bank0
                incf    fsr,f           ; increase pointer to point to next value
                movf    fsr,w           ; get pointer value
                sublw   blockbuff63     ; was this the last byte ?
                skpnc                   ; let's see
                goto    RD_LOOP         ; no, do loop again and read next data byte
                goto    TX_COMMANDEND   ; no, send checksum and ETX, clear command status byte

;--------------------------------------------------------------------------------------------------------------------------------------

RI                                      ; increment block number, then download record data in blocks of 64 bytes

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
RI_REQUEST      bank3                   ; check if there are any records
                movf    current_rec,w   ; get currently selected record number, zero for no selection yet or when no records available
                skpnz                   ; are there any records and has there been a record selected yet ?
                goto    EXIT_NAK        ; no, exit
                pagesel MEM_INC_RDBLOCK ; yes, make right program memory page selection
                call    MEM_INC_RDBLOCK ; copy data block to buffer in bank1, increase blocknumber, carry set when all blocks were done
                pagesel RD_RI_CODE      ; make right program memory page selection
                goto    RD_RI_CODE      ; from here same code as for RD command, save space

;--------------------------------------------------------------------------------------------------------------------------------------

RM                                      ; return the percentage of memory that is used
                                        ; value 0..100%

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
RM_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                pagesel MEM_USAGE       ; make right program memory page selection
                call    MEM_USAGE       ; **** get the percentage of used space of external eeprom memory into w register, bank? return
                pagesel TX_NUMBER       ; make right program memory page selection
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

;--------------------------------------------------------------------------------------------------------------------------------------

RN                                      ; number of records in datalogger memory
                                        ; value 0..20

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
RN_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                pagesel MEM_GETNUMRECS  ; make right program memory page selection
                call    MEM_GETNUMRECS  ; get the number of records stored in memory, value stored in num_records, bank1/3 return
                bank3                   ;
                movf    num_records,w   ; copy the number of records
                pagesel TX_NUMBER       ; make right program memory page selection
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

;--------------------------------------------------------------------------------------------------------------------------------------

RS                                      ; select one record for downloading and reset the block download position to the start of
                                        ; this record
                                        ; valid value range is 1..number of available records (maximum 20)

                btfsc   flags1,withdata ; is there any data ?
                goto    RS_COMMAND      ; yes, so we should use this data
RS_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                bank3                   ;
                movf    current_rec,w   ; get the number of the currently selected record
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

RS_COMMAND                              ; empty line to prevent illegal label error from MPLAB IDE
                pagesel MEM_GETNUMRECS  ; make right program memory page selection
                call    MEM_GETNUMRECS  ; get the number of records stored in memory, value stored in num_records, bank1/3 return
                pagesel GET_NUMBER      ; make right program memory page selection
                call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                iorlw   d'0'            ; see if number is zero without changing w register, the number should not be less than one
                skpnz                   ; is the number zero ?
                goto    EXIT_NAK        ; yes, exit
                bank3                   ; no, continue
                subwf   num_records,w   ; see if the new value is valid
                skpc                    ; is the new value more than the available number of records ?
                goto    EXIT_NAK        ; yes, do not update value and exit, be ready for next command
                call    GET_NUMBER      ; no, again read the data input number that goes with the command into the w register
                pagesel MEM_SELECTREC   ; make right program memory page selection
                call    MEM_SELECTREC   ; select record for downloading by value in w register and reset the block download pointer
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

RT                                      ; read start time and date of the currently selected record
                                        ; in the following format HH:MM:SS MM/DD/YY

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
RT_REQUEST      bank3                   ; no, check current record selection
                movf    current_rec,w   ; test for zero value
                skpnz                   ; are there any records and is there already a valid record selected ? 
                goto    EXIT_NAK        ; no, exit
                call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                pagesel MEM_GETTIMEDATE ; make right program memory page selection
                call    MEM_GETTIMEDATE ; store start time and date of currently selected record in the time and date buffer in bank2
                pagesel TX_TIMEANDDATE  ; make right program memory page selection
                call    TX_TIMEANDDATE  ; send time and date values from the buffer in bank2 as a string, bank2 return
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

RZ                                      ; get record size (in bits !) of currently selected record

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
RZ_REQUEST      bank3                   ; no, check current record selection
                movf    current_rec,w   ; test for zero value
                skpnz                   ; are there any records and is there already a valid record selected ? 
                goto    EXIT_NAK        ; no, exit
                call    TX_COMMANDSTART ; yes, send ACK, STX and command mnemonics
                pagesel MEM_GETRECSIZE  ; make right program memory page selection
                call    MEM_GETRECSIZE  ; store size (in bits) of currently selected record into registers numlow, nummiddle, numhigh
                pagesel TX_WNUMBER      ; make right program memory page selection
                call    TX_WNUMBER      ; transmit the 24 bit number in numlow(lsB), nummiddle and numhigh(msB), bank0 return
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

;--------------------------------------------------------------------------------------------------------------------------------------


SE                                      ; get serial number of device from location in bootloader area
                                        ; the four topmost words in PIC flash memory are reserved for storing serial numbers etc.
                                        ; after the write backdoor is closed again

                btfsc   flags1,withdata ; is there any data ?
                goto    SE_COMMAND      ; yes, so we should use this data
SE_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                call    SE_LOADADDRESS  ; bank2 return
                pagesel FLASHREAD       ; make right program memory page selection
                call    FLASHREAD       ; read flash program memory, address in eeadr/eeadrh, data in eedata/eedath, bank3 return
                bank2                   ;
                movf    eedata,w        ; get data value into wordlow and wordmiddle
                movwf   numlow          ;
                movf    eedath,w        ;
                movwf   nummiddle       ;
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmits the value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte


SE_COMMAND      call    GET_LNUMBER     ; read the data input number that goes with the command into the numlow and nummiddle register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                btfss   flags1,changese ; check the backdoor flag that allows the serial number to be changed
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                movlw   b'00111111'     ; words of pic program memory are 14 bits wide, strip upper two bits
                andwf   nummiddle,f     ;
                movf    numlow,w        ; copy data
                bank2                   ;
                movwf   eedata          ; low
                movf    nummiddle,w     ;
                movwf   eedath          ; high
                bcf     flags1,changese ; clear backdoor flag that allows serial number to be changed
                call    SE_LOADADDRESS  ; bank2 return
                pagesel FLASHWRITE      ; make right program memory page selection
                call    FLASHWRITE      ; write flash memory,addr. in eeadr/eeadrh,data in eedata/eedath, carry set on error, bank2 ret.
                pagesel EXIT_ACK        ; make right program memory page selection
                skpnc                   ; was the data written ok ?
                goto    EXIT_NAK        ; exit, there was a data write verification error
                goto    EXIT_ACK        ; done


SE_LOADADDRESS  bank2                   ;
                movlw   h'FF'           ; get low byte value
                movwf   eeadr           ; low byte
                movlw   h'1F'           ; get low byte value
                movwf   eeadrh          ; high byte
                return                  ; return in bank2

;-------------------------------------------------------------------------------------------------------------------------------------

SV                                      ; read firmware build version (for example: 1.66)

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
SV_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                movlw   fwv0            ; version bytes, defined at the very top of this program
                call    TX_BYTE         ;
                movlw   a'.'            ;
                call    TX_BYTE         ;
                movlw   fwv1            ;
                call    TX_BYTE         ;
                movlw   fwv2            ;
                call    TX_BYTE         ;
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

;--------------------------------------------------------------------------------------------------------------------------------------

V0                                      ; get values of all inputs (read only), separated by a horizonal tab (456  22  1014 ...)
                                        ; actual command mnemonic is V$, but we cannot use the dollar character in labels
                                        ; transmit order:
                                        ; RPM,speed,lambda,voltage,thermocouple,air,water,throttle,longitudinal,lateral,digital

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
V0_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
V0_RPM                                  ; prevent mplab ide errror
                pagesel CALC_RPM        ; make right program memory page selection
                call    CALC_RPM        ; yes, calculate the RPM value from the time interval between the input pulses, return in bank2
                movf    rpm_low,w       ; get low byte value
                movwf   numlow          ;
                movf    rpm_high,w      ; get high byte value
                movwf   nummiddle       ;
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmits the value of numlow and nummiddle as ascii characters
V0_TAB0         movlw   d'9'            ; seperator character is ascii horizontal tab character
                call    TX_BYTE         ; send separator character
V0_SPEED                                ; prevent mplab ide errror
                pagesel CALC_SPEED      ; make right program memory page selection
                call    CALC_SPEED      ; calculate speed value from time interval between input pulses, return in bank2
                movf    speed,w         ; get value
                pagesel TX_NUMBER       ; make right program memory page selection
                call    TX_NUMBER       ; transmits the value of w register as ascii characters
V0_TAB1         movlw   d'9'            ; seperator character is ascii horizontal tab character
                call    TX_BYTE         ; send separator character
V0_ANALOG_CHAN  clrf    adchannel       ; start at channel zero
V0_LOOP         movf    adchannel,w     ; select channel
                pagesel GET_ANALOG      ; make right program memory page selection
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmit value of numlow and nummiddle as ascii characters
V0_TAB2         movlw   d'9'            ; seperator character is ascii horizontal tab character
                call    TX_BYTE         ; send separator character
                incf    adchannel,f     ; select next channel
                movlw   d'8'            ; there's eight analog channels
                subwf   adchannel,w     ; see if we have done all
                skpz                    ; was this the last measurement ?
                goto    V0_LOOP         ; no, next
V0_DIGITAL      bank0                   ; yes,
                movf    portb,w         ; get the status of portb
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; exit, send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

VA                                      ; get value of air temperature analog input (read only)

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VA_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'3'            ; analog input channel 3
                pagesel GET_ANALOG      ; make right program memory page selection
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmit value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

VD                                      ; get the value of digital portb, which are the digital inputs (upper two bits are outputs)
                                        ; read: bit7=green led, red led, brdsuppl,tcdisc,laptime,brake,mark,_run_/log=bit0
                                        ; write: only upper two bits (the leds) are written

                btfsc   flags1,withdata ; is there any data ?
                goto    VD_COMMAND      ; yes, so we should use this data to write to the eeprom location
VD_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                bank0                   ;
                movf    portb,w         ; get the status of portb
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

VD_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                bank0
                xorwf   portb,w         ; calculate the difference between new data and the actual status
                andlw   b'11000000'     ; mask, we only should only change bits 6 and 7 which are outputs
                xorwf   portb,f         ; flip bits if they are not the same and write the new value to portb
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

VE                                      ; get value of lateral g-sensor analog input (read only)

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VE_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'7'            ; analog input channel 7
                pagesel GET_ANALOG      ; make right program memory page selection
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmit value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

VH                                      ; get value of throttle (handlebar) analog input (read only)

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VH_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'5'            ; analog input channel 5
                pagesel GET_ANALOG      ; make right program memory page selection
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmit value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

VI                                      ; set or read the eight leds of port D (shift light leds)

                btfsc   flags1,withdata ; is there any data ?
                goto    VI_COMMAND      ; yes, so we should use this data
VI_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                bank0                   ;
                movf    portd,w         ; get the current leds status
                call    TX_NUMBER       ; transmits the value of w as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

VI_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                bank0                   ;
                movwf   portd           ; set leds to new value
                goto    EXIT_ACK        ; done


;--------------------------------------------------------------------------------------------------------------------------------------

VL                                      ; get value of lambda analog input (read only)

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VL_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'0'            ; analog input channel 0
                pagesel GET_ANALOG      ; make right program memory page selection
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmit value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

VO                                      ; get value of longitudinal g-sensor analog input (read only)

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VO_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'6'            ; analog input channel 6
                pagesel GET_ANALOG      ; make right program memory page selection
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmit value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

VR                                      ; returns value of engine rpm, calculated from pulses on CCP1 input

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VR_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                pagesel CALC_RPM        ; make right program memory page selection
                call    CALC_RPM        ; calculate the RPM value from the time interval between the input pulses, return in bank2
                movf    rpm_low,w       ; get low byte value
                bank0                   ;
                movwf   numlow          ;
                bank2                   ;
                movf    rpm_high,w      ; get high byte value
                bank0                   ;
                movwf   nummiddle       ;
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmits the value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

;--------------------------------------------------------------------------------------------------------------------------------------

VS                                      ; returns value of vehicle speed in km/hr, calculated from pulses on CCP2 input

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VS_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                pagesel CALC_SPEED      ; make right program memory page selection
                call    CALC_SPEED      ; calculate speed value from time interval between input pulses, return in bank2
                movf    speed,w         ; get value
                pagesel TX_NUMBER       ; make right program memory page selection
                call    TX_NUMBER       ; transmits the value of w register as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

;--------------------------------------------------------------------------------------------------------------------------------------

VT                                      ; get value of thermocouple analog input (read only)

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VT_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'2'            ; analog input channel 2
                pagesel GET_ANALOG      ; make right program memory page selection
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmit value of numlow and nummiddle as ascii characters, return in bank0
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

VV                                      ; get value of voltage analog input (read only)

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VV_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'1'            ; analog input channel 1
                pagesel GET_ANALOG      ; make right program memory page selection
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmit value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

VW                                      ; get value of water temperature analog input (read only)

                btfsc   flags1,withdata ; is there any data ?
                goto    EXIT_NAK        ; yes, exit
VW_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                movlw   d'4'            ; analog input channel 4
                pagesel GET_ANALOG      ; make right program memory page selection
                call    GET_ANALOG      ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
                pagesel TX_LNUMBER      ; make right program memory page selection
                call    TX_LNUMBER      ; transmit value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command flag

;--------------------------------------------------------------------------------------------------------------------------------------

XA                                      ; set external eeprom address, input is ascii string with 16 bit number

                btfsc   flags1,withdata ; is there any data ?
                goto    XA_COMMAND      ; yes, so we should use this data
XA_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank1                   ;
                movf    iicalow,w       ; copy iic address registers to rs232 number receive/transmit registers
                bank0                   ;
                movwf   numlow          ;
                bank1                   ;
                movf    iicahigh,w      ;
                bank0                   ;
                movwf   nummiddle       ;
                call    TX_LNUMBER      ; transmits the value of numlow and nummiddle as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

XA_COMMAND      call    GET_LNUMBER     ; read the data input number that goes with the command into the numlow and nummiddle register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                btfsc   nummiddle,7     ; size of the eeproms is 32k x 8 (256kbit), therefore the address maximum is 32767
                goto    EXIT_NAK        ;
                movf    numlow,w        ; store address in iic address registers
                bank1                   ;
                movwf   iicalow         ;
                bank0                   ;
                movf    nummiddle,w     ;
                bank1                   ;
                movwf   iicahigh        ;
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

XC                                      ; select external eeprom chip, input is ascii character with 8 bit number (0..7)

                btfsc   flags1,withdata ; is there any data ?
                goto    XC_COMMAND      ; yes, so we should use this data
XC_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                bank1                   ;
                movf    iicchip,w       ; get number of selected chip
                call    TX_NUMBER       ; transmits the value w register as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

XC_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                sublw   d'7'            ; maximum number of eeprom chips is eight, therefore the number should be from 0..7
                skpc                    ; is the number larger than 7 ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                sublw   d'7'            ; no, restore w register content to its previous value
                bank1                   ;
                movwf   iicchip         ; store new chip select value
                goto    EXIT_ACK        ; done


;--------------------------------------------------------------------------------------------------------------------------------------

XD                                      ; read/write data byte from external eeprom value
                                        ; write: input is ascii string with 8 bit number

                btfsc   flags1,withdata ; is there any data ?
                goto    XD_COMMAND      ; yes, so we should use this data
XD_REQUEST      call    TX_COMMANDSTART ; send ACK, STX and command mnemonics
                pagesel IIC_RDBYTE      ; make right program memory page selection
                call    IIC_RDBYTE      ; read data byte from external eeprom, address is in iicchip, iicalow and iicahigh
                bank1                   ;
                movf    iicdata,w       ; store data byte in w register
                bank0                   ;
                pagesel TX_NUMBER       ; make right program memory page selection
                call    TX_NUMBER       ; transmit the value of the w register as ascii characters
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

XD_COMMAND      call    GET_NUMBER      ; read the data input number that goes with the command into the w register
                skpnc                   ; errors reading number ?
                goto    EXIT_NAK        ; yes, exit and be ready for next command
                bank1                   ;
                movwf   iicdata         ; store data byte from w register
                pagesel IIC_WRBYTE      ; make right program memory page selection
                call    IIC_WRBYTE      ; write byte in iicdata to external eeprom, address in iicchip, iicalow and iicahigh
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done

;--------------------------------------------------------------------------------------------------------------------------------------

XP                                      ; read/write data 1 block of 64 bytes from external eeprom
                                        ; write 64 bytes of data from RS232 input buffer
                                        ; **** the receive routine prevents the character h'03' to be included in the data !

                btfsc   flags1,withdata ; is there any data ?
                goto    XP_COMMAND      ; yes, so we should use this data
XP_REQUEST      call    TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
                pagesel IIC_RDBLOCK     ; make right program memory page selection
                call    IIC_RDBLOCK     ; read 64 data bytes from external eeprom, address in iicchip, iicalow and iicahigh
                bcf     status,irp      ; make sure upper bit in bank address is zero (select bank 0 and 1)
                movlw   h'AF'           ; start position of data in buffer in bank1
                movwf   fsr             ; point to this address
XP_LOOP1        movf    indf,w          ; read the data byte at the right position in the buffer
                pagesel TX_BYTE         ; make right program memory page selection
                call    TX_BYTE         ; transmit the value of the w register
                incf    fsr,f           ; increase pointer to point to next value
                movf    fsr,w           ; use pointer value
                sublw   h'EE'           ; was this the last byte ?
                skpnc                   ; let's see
                goto    XP_LOOP1        ; no, do loop again and read next data byte
                goto    TX_COMMANDEND   ; send checksum and ETX, clear command status byte

XP_COMMAND      bank0                   ; check that we actually got 64 bytes
                movlw   rx_buffer65     ; position of last data byte
                subwf   rx_maxpos,w     ; test length
                skpz                    ; did we get exactly 64 data bytes ?
                goto    EXIT_NAK        ; no, exit and be ready for next command
                movlw   h'2F'           ; yes, copy the 64 databytes from databuffer in bank0 to the block buffer in bank1
                movwf   fsr             ; point to the start position of the data buffers in bank0 and bank1 (!)
                bcf     status,irp      ; make sure upper bit in bank address is zero (select bank 0 and 1)
XP_LOOP2        movf    indf,w          ; read the data byte at the right position in the buffer
                bsf     fsr,7           ; the block buffer is in bank1 (hex 80 to hex FF)
                movwf   indf            ; write the data byte
                incf    fsr,f           ; increase pointer to point to next value
                bcf     fsr,7           ; the receive databuffer is in bank 0 (hex 00 to hex 7F)
                movf    fsr,w           ; use pointer value
                sublw   h'6E'           ; was this the last byte ?
                skpnc                   ; let's see
                goto    XP_LOOP2        ; no, do loop again and read next data byte
                pagesel IIC_WRBLOCK     ; make right program memory page selection
                call    IIC_WRBLOCK     ; yes, write 64 bytes to external eeprom, address in iicchip, iicalow and iicahigh
                pagesel EXIT_ACK        ; make right program memory page selection
                goto    EXIT_ACK        ; done


;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------


                org     h'1800'         ; start of program memory page 3


;--------------------------------------------------------------------------------------------------------------------------------------
; command interpreter: find and go execute selected command 
;--------------------------------------------------------------------------------------------------------------------------------------
;
;                                               to do commands are marked with a '>' character
;                                               non optimized commands are marked with '***'
;
; mnemonic      read/   value range or          description
;               write   example
;
;
; AU            R(/W)   Andries C. Tip          returns software author, a write opens the backdoor to change serial number
;
; CL            R/W     05/31/04 11:23:47       clock time and date in the following format MM/DD/YY HH:MM:SS
; CA            R/W     0..63                   clock static ram address byte (address wraps around)
; CD            R/W     0..255                  clock static ram data byte at address specified by CA
;
; EA            R/W     0..255                  address location for PIC internal eeprom                
; ED            R/W     0..255                  data of the PIC internal eeprom at the address specified by EA
; ER            R/W     0..255                  error flag bits, bits will be set when errors occur during logging
;
; FA            R/W     32                      air temperature logging frequency
; FB            R/W     16                      brake switch logging frequency
; FE            R/W     8                       lateral acceleration logging frequency
; FH            R/W     0                       thermocouple logging frequency
; FL            R/W     2                       lambda logging frequency
; FM            R/W     1                       mark switch logging frequency
; FO            R/W     0.5                     longitudinal acceleration logging frequency
; FR            R/W     0.25                    rpm logging frequency
; FS            R/W     0                       speed logging frequency
; FT            R/W     4                       throttle logging frequency
; FV            R/W     0                       voltage logging frequency
; FW            R/W     0                       water temperature logging frequency
;
; ID            R       Motorcycle Datalogger   returns device identification string
;
; >KA           R/W     0..16383                engine stationary RPM adjust, left half of leds are on, 0=off
; >KB           R/W     0..16383                engine high RPM adjust. right half of leds are on, 0=off 
; KC            R/W     1..65535                wheel circumference [millimeters] used in speed calculation
; >KN           R/W     1..255                  puls count setting: Number of notches/pulses per revolution of wheel
; >KP           R/W     2                       pulses per crankshaft revolution, used in rpm calculation
; KR            R/W     0..16383                max RPM rate, leds will flash above this rate
;
; >LS           R/W     0..255                  select logging start condition (Rotary switch, Mark Button, Speed, RPM)
; >LV           R/W     0..16363                select value for logging start condition (when RPM/Speed)
;
; >PA           R/W     1..7/13                 select the desired logging resolution of a channel
;
; RN            R       0..20                   number of records in datalogger memory
; RS            R/W     0..19                   select a record for downloading
; RT            R       07/28/04 12:32:04       record start time and date in the following format MM/DD/YY HH:MM:SS
; RZ            R       93645543                get record size (in bits !) and reset download position
; RD            R       6%gr'@:sE....           download record data in blocks of one block (64 bytes)
; RI            R       6%gr'@:sE....           increment block, then download record data in blocks of one block (64 bytes)
; RC            W                               clear all record memory 
; >RM           R       0..100                  memory usage/maximum memory address ??
;
; >SD           R       14/28/04                device programming date, stored in flash program memory of bootloader 
; SE            R(/W)   0..16383                device serial number in top of flash program memory, see AU command
; >ST           R       0..255                  datalogger status (busy, idle, full, error, etc.)
; SV            R       1.55                    returns firmware version
;
; V$            R       0..1023                 values of all inputs separated by a tab (234 444 ...)
; VA            R       0..1023                 analog input channel 3: air temperature (NTC)
; VD            R/W     0..255                  status of digital portb
; VE            R       0..1023                 analog input channel 7: lateral G-sensor
; VH            R       0..1023                 analog input channel 5: throttle (0..5V)
; VI            R/W     0..255                  status of leds at portd functioning as shift light and rpm counter
; VL            R       0..1023                 analog input channel 0: lambda sensor
; VO            R       0..1023                 analog input channel 6: longitudinal G-sensor
; VR            R       0..16383                RPM rate (ccp1 pulse input)
; VS            R       0..255                  Speed [km/hr] from ccp2 pulse input
; VT            R       0..1023                 analog input channel 2: K-type thermocouple
; VV            R       0..1023                 analog input channel 1: voltage in 0..5 V
; VW            R       0..1023                 analog input channel 4: water temperature (NTC) 
;
; XA            R/W     0..32767                external eeprom address
; XC            R/W     0..7                    select external eeprom chip
; XD            R/W     0..255                  data byte of the external eeprom at the position specified by XA and XC
; XP            R/W     ABC@%432_;!...          block size read or write (64 bytes) external eeprom
;
;--------------------------------------------------------------------------------------------------------------------------------------


EXEC_COMMAND                            ; go look in buffer to find out which command was sent and has to be executed
                                        ; a command starts with two letters which may be followed by one or more data bytes

                                        ; remember CTS has already been cleared to hold data stream from computer
                                        ; CTS will be set again after command execution to re-enable data reception

                bank0                   ;
                bcf     portb,led_red   ; turn off red status led
                bsf     portb,led_green ; turn on green status led
A?              movlw   a'A'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    A_              ;
C?              movlw   a'C'            ; does the command start with the letter 'C' ?
                subwf   rx_buffer00,w   ; compare to first command character (first byte in buffer)
                skpnz                   ; let's see
                goto    C_              ; yes it does, go to all commands starting with the letter 'C'
E?              movlw   a'E'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    E_              ;
F?              movlw   a'F'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    F_              ;
I?              movlw   a'I'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    I_              ;
K?              movlw   a'K'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    K_              ;
L?              movlw   a'L'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    L_              ;
R?              movlw   a'R'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    R_              ;
S?              movlw   a'S'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    S_              ;
V?              movlw   a'V'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    V_              ;
X?              movlw   a'X'            ;
                subwf   rx_buffer00,w   ;
                skpnz                   ;
                goto    X_              ;
                pagesel EXIT_NAK        ; make right program memory page selection
                goto    EXIT_NAK        ; no command mnemonic matched, exit

;--------------------------------------------------------------------------------------------------------------------------------------



A_                                      ; empty line to prevent mplab editor error
                pagesel COMMANDS        ; make right program memory page selection
                movlw   a'U'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    AU              ;
                goto    EXIT_NAK        ; exit_nak is on same page as commands so pagesel is valid

C_                                      ; empty line to prevent mplab editor error
                pagesel COMMANDS        ;
                movlw   a'A'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    CA              ;
                movlw   a'D'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    CD              ;
                movlw   a'L'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    CL              ;
                goto    EXIT_NAK        ;

E_                                      ;
                pagesel COMMANDS        ;
                movlw   a'A'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    EA              ;
                movlw   a'D'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    ED              ;
                movlw   a'R'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    ER              ;
                goto    EXIT_NAK        ;

F_                                      ;
                pagesel COMMANDS        ;
                movlw   a'A'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FA              ;
                movlw   a'B'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FB              ;
                movlw   a'E'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FE              ;
                movlw   a'H'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FH              ;
                movlw   a'M'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FM              ;
                movlw   a'L'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FL              ;
                movlw   a'O'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FO              ;
                movlw   a'R'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FR              ;
                movlw   a'S'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FS              ;
                movlw   a'T'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FT              ;
                movlw   a'V'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FV              ;
                movlw   a'W'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    FW              ;
                goto    EXIT_NAK        ;

I_                                      ;
                pagesel COMMANDS        ;
                movlw   a'D'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    ID              ;
                goto    EXIT_NAK        ;

K_                                      ;
                pagesel COMMANDS        ;
                movlw   a'C'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    KC              ;
                movlw   a'P'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    KP              ;
                movlw   a'R'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    KR              ;
                goto    EXIT_NAK        ;

L_                                      ;
                pagesel COMMANDS        ;
                movlw   a'S'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    LS              ;
                movlw   a'V'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    LV              ;
                goto    EXIT_NAK        ;

R_                                      ;
                pagesel COMMANDS        ;
                movlw   a'C'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    RC              ;
                movlw   a'D'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    RD              ;
                movlw   a'I'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    RI              ;
                movlw   a'M'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    RM              ;
                movlw   a'N'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    RN              ;
                movlw   a'S'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    RS              ;
                movlw   a'T'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    RT              ;
                movlw   a'Z'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    RZ              ;
                goto    EXIT_NAK        ;

S_                                      ;
                pagesel COMMANDS        ;
                movlw   a'E'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    SE              ;
                movlw   a'V'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    SV              ;
                goto    EXIT_NAK        ;

V_                                      ;
                pagesel COMMANDS        ;
                movlw   a'$'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    V0              ;
                movlw   a'R'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VR              ;
                movlw   a'S'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VS              ;
                movlw   a'A'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VA              ;
                movlw   a'D'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VD              ;
                movlw   a'E'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VE              ;
                movlw   a'H'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VH              ;
                movlw   a'I'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VI              ;
                movlw   a'L'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VL              ;
                movlw   a'O'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VO              ;
                movlw   a'T'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VT              ;
                movlw   a'V'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VV              ;
                movlw   a'W'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    VW              ;
                goto    EXIT_NAK        ;

X_                                      ;
                pagesel COMMANDS        ;
                movlw   a'A'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    XA              ;
                movlw   a'C'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    XC              ;
                movlw   a'D'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    XD              ;
                movlw   a'P'            ;
                subwf   rx_buffer01,w   ;
                skpnz                   ;
                goto    XP              ;
                goto    EXIT_NAK        ;

;--------------------------------------------------------------------------------------------------------------------------------------


                org     h'1EA4'         ; start of bootloader version 1.1 from file boot31.asm is at h'1EA5' !!!

                nop                     ; force assembler to notice the start of this page
                                        ; we switched off the assember error messages but we want to see if we cross boundaries


;--------------------------------------------------------------------------------------------------------------------------------------

                end                     ; tell the assembler to stop