please dont rip this site

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





file: /Techref/member/AT-planet-T9/datalogger.htm, 425KB, , updated: 2011/2/23 21:13, local time: 2024/12/27 06:43, owner: AT-planet-T9,
TOP NEW HELP FIND: 
3.139.86.53:LOG IN

 ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://ecomorder.com/Techref/member/AT-planet-T9/datalogger.htm"> Datalogger</A>

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type a nice message (short messages are blocked as spam) in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.


Link? Put it here: 
if you want a response, please enter your email address: 
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
Did you find what you needed?