CHAPTER 9

Nuts and Bolts

We have talked a lot about functional blocks, but there is a lot of reused code which is common to all modules, the nuts and bolts which make the program possible and hold it together!

We must include in these categories two areas about which we have said very little.

  1. MATHEMATICS
  2. LOGIC

Math routines

All of the basic routines for hexadecimal maths
  1. Multiplication
  2. Division
  3. Addition
  4. Subtraction

are freely available on the internet. They come in a multitude of shapes, 4 , 8, 16,24,32…bit.
There are just two little problems. Many of them aren't what you want and, many of them are theoretical, and hard to put into assembler code ( a euphemism for maybe they never have worked).
Another more subtle problem is that some work with the example figures but not with others…. Gulp!!!
We need routines that work with all figures inside the bit range required.

So you can imagine that this was a long process narrowing down what was available and what was required.

Fortunately in the PICLIST. COM, you will find the routines that I have used and many others, which also work, but whose operation is a bit too subtle to be included in this SIMPLE first time code effort.

LOGIC routines

These have been kept to a minimum . Basically the Bike computer uses
An EQUAL TEST, which is the xorwf instruction. STATUS, Z

A less than equal =< coupled with greater than > test
Which use these instructions e.g. subwf dtothi,w followed by btfsc STATUS,C


These, AND MANY MORE, are well explained in the articles in PICLIST.COM.

Conversion routines

The three main types that had to be invented were converting

  1. HEX to DECIMAL
  2. HEX FRACTIONS to DECIMAL FRACTIONS
  3. NUMBER SCALING

HEX to DECIMAL

This we have already discussed h2d in previous paragraphs but the basic routine is used in the three calculation modules for, SPEED, DISTANCE and AVERAGE SPEED.

You should be able to follow the conversion routines after a little bit of study. The notes in each routine to say where you are in the process are abundant.

HEX FRACTIONS to DECIMAL FRACTIONS

This merely is an extension of the previous routines with a little bit of reasoning to change hex remainder and fractions into decimal fractions.

The fraction routines are needed because the pics don't handle directly fractions, so they have to be changed into whole numbers by suitable multiplication.

NUMBER SCALING

We do a lot of multiplication and division in the three main calculations for Speed, Distance and Average Speed.

But we have a practical 16bit number limit. The numbers can't be bigger than 65536.

If they are, then we need to add more registers, and probably bigger calculation routines able to handle more than 16bits. So I decided to try and stay as far as possible within the 16bit routines.

In some places you divide and the result of the division has to be multiplied by another number. For example 2000.

So you divide your first two numbers. The result is say ten.
Then the next operation has no problem because 10 x 2000 is 20000 which is< 65536.

However if the first division had given as a result 300, then we have a problem.

300 x 2000 = 600000 way above the limit of 65536 we need to scale somewhere.

The scale factor here would be about 20. But this is one of those things that is best seen following the steps the code takes , say in the SPEED block.

Basically the number is tested for size and divided down until its product with a reference number is smaller than 65536. But as we said above, follow the steps taken in the actual code block to see how the scaling is done to solve a specific application problem.

LCD Setup, Signalling and data transfer routines

The LCD itself has on board a complex controller chip whose job is to interface with a uController, in this case our 16F84A and steer the incoming data messages to the correct position for display.

There are two basic operation modes; 4 bit and 8 bit. Most applications seem to use the 4 bit mode as it will free 4 lines of a uController to do other things.

The data is strobed into the LCD using appropriate signals on the E and RS lines. The protocols are also time sensitive. They have to be as long as the minimum times quoted on the LCD data sheets. Here lies a problem.

The LCD chosen is very popular. It has many clones. SOME of the clones need EVEN longer times. This means that the typical DELAYS programmed could be insufficient.

Before anything can be displayed, the LCD needs to be initialised. This is a really slow process taking about 50 to 100ms. The number of times the routines are repeated during the initiation sequences seems a little exaggerated but it has to be that way to get the LCD up and running, it is exactly what the manufacturers spell out on their data sheets.

Each command or data interchange between the PIC and the LCD has it's corresponding minimum time. These have all to be scrupulously observed or the process fails.

That is the reason why there are so many different time blocks within the delay module plus some additional ones for other tests.

All of the routines can be found on the PICLIST.COM site. I take my hat off to whoever sat down and worked them out first time round, a really heroic and Herculean task.

Automatic menu (2)

The operation of Automatic menu was explained previously under the heading of that name when we were looking at the Flow Chart.

However this was one of two possibilities in the original project, the other, manual function selection needed two push buttons plus the corresponding code so that was dropped as the PIC was running out of space.

There is nothing special about the way the menu presents the three main features. Their combinations features to the LCD can be changed if you prefer. For example, maybe you would like to see at once Average Speed and Total distance, or maybe Speed and total distance.

There are many combinations possible. Of course if you do change then don't forget to follow through into each module that is being used to see that there is no other unwanted effects.

Bike3.asm

We can now study the complete program which is called bike3.asm

With the help of the previous Flow Chart which shows the sequence of major functional blocks of the program, and reading through the code below, it should now be possible to understand, hopefully fairly easily, how the code blocks have been cobbled together.

Made to implement the basic equation we started out with.

Speed = Distance /Time.









;----------------------------------------------------------------------;
; bike3.asm   Combines: Time , Dist, Av velocity, Inst Vel  
;             Fred Maher             1st March 2002  
;   BASIC BICYCLE  COMPUTER WITH THESE FUNCTIONS              
;----------------------------------------------------------------------;

; 1          Trip Time    HRS:MIN:SEC
; 2          Trip Dist    000km 00m
; 3          Trip AvSpd   00.00km/hr
; 4          Speed        00.00km/hr    



 

;---------------------------------------------------------------------               
;-------                 HEADER                ----------
;---------------------------------------------------------------------

;                      LCD MESSAGES
; position at beginning of 1st line col 0  movlw H'80 
; position at beginning of second line     movlw H'C0'
       

      LIST P=16F84           ;  16F84 Runs at 4.096 MHz
      INCLUDE "p16f84A.inc"
      __CONFIG    _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON  
  
      ERRORLEVEL -224        ;  suppress annoying message because of tris
      ERRORLEVEL -302        ;  suppress message because of page change

; Define Information
     #DEFINE RS PORTA, 2
     #DEFINE E  PORTA, 3
    ;#DEFINE TOGGLESW PORTB, 6  ; not used at the moment
    ;#DEFINE LED PORTB, 5
;  Macro

;--------------------------------------------------------------- 
            CBLOCK     0CH   ; from 0C to 4F = 67
;---------------------------------------------------------------

;                   MATH ROUTINES    

;                16 X16 Mult , --> 32
;     mah mal x mbh mbl = mq4 mq3 mq2 mq1 ( mq4 highest)
 mq4
 mq3
 mq2
 mq1        
 mbh
 mbl
 mah
 mal 
;                   16 / 16 Div --> 16
 denlo          
 denhi	   
 numlo  	  
 numhi	  
 rmdrlo 	    
 rmdrhi 	  
 reshi
 reslo
 ;temp       also used in nybble

; Addition 16+16 out max 65536 (no carry)
; Subtraction q1_16-q2_16 out r_16; neg numbers not allowed
           
 q1hi		  	
 q1lo		  
 q2hi		  		 
 q2lo		  
 rhi		  
 rlo		  
;------------    Distance         -----------------------
 
 dtotlo       ; total trip distance in metres
 dtothi 	  ; dtothi/lo hold a max of 65km 536m,  
 dtothi2      ; with this added max now 16777km 
 dist54       ; corrects +2m every 54m (27x2.0747 =56)
 dm
 dm10
 dm100
 dm1000
 dm10000
 dm100000

;------------   Average velocity Vav    ------------------- 
 
 ;vavhi
 ;vavlo
 ;divrat
 ;stemphi     ;second info passed to stemphi/lo
 ;stemplo    
 ;dtemphi     ;dist info passed to dtemphi/lo	
 ;dtemplo
 m
 m10
 m100
 m1000
;---------   Time and trip time        --------------------
;
 sectotlo        ; total trip seconds 
 sectothi        ; max 65536 = aprox 18hrs  
 sec             ; seconds digit
 sec10           ; 10's of second digit
 min             ; minutes digit
 min10           ; 10's of minutes digit
 hr              ; hours digit
 hr10            ; 10's of hours digit
 oldsec          ; holds last value of sec
 cntmsec         ; count ms
 TMR18           ; TMR0 217* TMR18 18 = 1 SEC

;----        isr interrupt service routine push pop  -------
 w_temp          ; W isr var
 status_temp     ; STATUS isr var
 fsr_temp        ; FSR isr var

 
; ---------  Instantaneous Velocity  Vin  --------------------
 vinhi
 vinlo
 sectemp
 oldtemp
 totmslo
 totmshi
 totms2lo
 totms2hi
 mshi
 mslo
 spdflg   ; 1st pass =0 2nd pass =1, diff is time between pulses

  

;--------         Miscellaneous     ------------------
 temp            ; a temporary used in divide and nybble 
 count
 menu

 

       ENDC     ; end of definition block
;-------------------------------------------------------------------

          
   ORG 0        ; start at location 0
   goto main    ; jump over to main routine       
   ORG 4
   goto Isr     ; jump to interrupt routine


;----------------------------------------------------------------------;
;                           The Main routine                           ;
;----------------------------------------------------------------------;

; --------       THE   MAIN MODULES    ---------

      ;  Isr       stores elapsed TRIP time and generates hr:min:sec
      ;  Disptime  TRIP time, to max 99:59:59
      ;  Dist      Calculates total TRIP distance
      ;  Dispdist  TRIP distance to a max of 999km 999m
      ;  Velav     Average velocity, Totdist/totsecs max 99km/hr 
      ;  Dispvav   Average TRIP speed up to  99.99 km/hr
      ;  Velocity  Speed  from 2m/(time diff of pulse2-pulse1)
      ;  Dispd     Speed  up to 99.99km/hr
       
; NOTE. Speed is your  calculated velocity  every second
       
; --------    more to come??? NO, running out of space         ---------


;-----------------------------------------------------------------------
;                           M A I N 
;-----------------------------------------------------------------------


main:
       call Init               ; Initialize ports, set up timer
       call Initlcd          ; Initialize the LCD DUMMY

  
Initend:    
 

;------------------------------------------------------------------
; Changesec Changewhlpulse wait for interrupts from newsec or newwheelpulse
; -----------------------------------------------------------------

        clrf menu   ; initially set to 0 menu loop
        clrf spdflg  ; Initially = 0 2nd =  sub for time from zero 
                         ; every new sec, set to zero 

;              CHANGESEC  LOOPS TILL SEC CHANGES  
   
      ;clrw                          ;DUMMY 
      ;xorwf sec,w              ;DUMMY 
      ;btfsc STATUS,2       ;DUMMY 
      ;incf sec,f     	        ;DUMMY 

Changesec:         	 ;loops checking new sec and new wheel pulse 
     movf  oldsec, w 
     xorwf sec,w     
                  ; if equal, w= 0 and Z bit=1
    btfsc STATUS,2    ; test Z bit
    goto  Changesec  ; no change, loop
    movf  sec,w          ; sec has  increased, update
    movwf oldsec       ; sec and oldsec are = again 
    incf  menu,f          ; increases menu every sec 
		  

	    ; Note1, sectotlo/hi are updated every sec in ISR module
	    ; Note2, Decimal time also created in ISR module
                             ; but time display for LCD  controlled from
                             ; the automatic menu below
	 
      call Disptime   ;TRIP time,  will appear on LCD top line

Changewhp:          ; 1st detected wheelpulse starts menu sequence                 
	 			    
      clrf PORTB
      bcf PORTB,5   ; start with green LED off
loop:
      btfss PORTB, 4  ;(PUT BTFSC FOR TEST); i/p hi,?DUMMY
      goto loop            ; not yet
                                 ; i/p hi detected 
  		 ;green LED flashes with every wheel pulse    
      bsf PORTB, 5    ; LED on
                                ; wait a while to make sure switch has 
                                ; settled   
      movlw D'10'      ; wait about 10 msec
      call nmsec
	 
      btfsc PORTB, 4  ; will be lo (0) when finished
      goto $ -1            ; still low
                                ; now must wait a make sure bouncing stopped
      movlw D'10'       ; 10 milliseconds
      call nmsec
                                ; and check again
      btfsc PORTB, 4 ; if set, not finished
      goto $ -5            ; still hi start debounce  wait again
Tp1:                         ; green LED ready for next wheel pulse
      bcf PORTB,5	 ; i.e. LED off
      movf PORTB,w  ; reading to clear
	
  
  					
;              AUTOMATIC MENU: (LCD Top line always time)  
;              --------------
  		; menu selects at 10sec intervals in rotation
      		; Speed            menu  1 to 10
      		; Dist ance       menu 11 to 20
      		; Velav Avg vel menu 21 to 30 
         
                          ; MENU 0 TO 10

; as speed ( Speed) is stand alone, no sense in calculating it
; if it is not going to be displayed when menu is >10.
; BUT if SPEED AVERAGING IS TO BE USED,  kill off  the lines below that bypass
; the SPEED calc module

Menucheck:
      movlw 0x0A          ;check menu > 10  (to meet >10 AND =<20)
      subwf menu,w
      btfss STATUS,C   ;compare with 10
      goto $+2               ;menucount =< 10 not yet got to 11 jump to Speed
      goto Distblk 
      
      call Speed            ; Calculates instant speed,(time between pulses)
      movlw 0x0A         ; DUMMY, was  movlw 0xA
      subwf menu,w
      btfss STATUS,C  ;compare with 10
      call Spdflgcheck  ;menucount =< 10 ; also see 0-1 spdflg  
      goto Distblk

Spdflgcheck:
      clrw  
      xorwf spdflg,w
      btfsc STATUS,Z
      call Dispd        ;menucount =< 10 AND  spdflg =1
      Return

Distblk:
      call Dist        ;menucount  > 10 skip Dispd update Dist


	                          ; MENU 11 TO 20
      movlw 0x0A       ;check menu > 10  (to meet >10 AND =<20)
      subwf menu,w
      btfss STATUS,C   ;compare with 10
      goto $+5         ;menucount =< 10 not yet got to 11 jump to velav
      movlw 0x14
      subwf menu,w
      btfss STATUS,C   ;compare with 20
      call Dispdist    ;menucount =< 20  
      call Velav       ;menucount  > 20 skip dispdist update  Velav
 

                          ; MENU 20 TO 30
      movlw 0x14         ;check menu > 20  (to meet >20 AND =<30)
      subwf menu,w
      btfss STATUS,C  ;compare with 20
      goto $+5             ;menucount =< 20 not yet got to 21 jump to NEXT
      movlw 0x1E
      subwf menu,w
      btfss STATUS,C  ;compare with 30
      call Dispvav         ;menucount =< 30 
      movlw 0x1F        ;if it has reached 31 reset menu, before return  
      xorwf menu,w      
      btfss STATUS,Z   
      goto $ +2           ; not 31 goto changewhp1 direct 
      clrf menu           ; is 31, reset menu before changewhp1

Menuend: 
                               ; before changewhp jump, check that sec has not updated
                               ; if it has , jump back to changesec routine
      movf  oldsec, w 
      xorwf sec,w          ; if equal, w= 0 and Z bit=1
      btfsc STATUS,2   ; test Z bit
      goto  Changewhp ; no sec change, check whpulse change 
      goto  Changesec  ; a new sec starts measurements  
  
                                   ; loop while NOT newsec
; ---------------------------------------------------------------
;                          End Main
; ----------------------------------------------------------------

    



 


;----------------------------------------------------------------------;
; ISR, increments TMR0 by 1 every 256 Ásec. Basically just             ;
; reset the INTCON and TMR0 bits. The FSR,w, STATUS push pop kept      ;
;----------------------------------------------------------------------;
Isr:
            movwf w_temp             ; save W
            swapf STATUS,W        ; save status
            movwf status_temp      ; without changing flags
            swapf FSR,W               ; save FSR
            movwf fsr_temp           ; without changing flags

                                     ;256us * 217 * 18 = 0.999936 sec.   
                                     ;1hr_err = 0.23sec or 1 sec in 4hr

  ;The time loop starts  with TMR0 loaded with  (256-217)=39 After 217
  ;steps interrupt is set, TMR0 rolls to zero and the TMR18 inc +1  

            movlw D'39'           ;39 = 0x27 
            movwf TMR0
            incf  TMR18,f
            movlw D'18'          ; DUMMY18 = 0x12 
            xorwf TMR18,w    ; if equal, w= 0 and Z bit=1
            btfss STATUS,2   ; test Z bit
            goto  restore        ; NOT 18 so pop stack and return to main 
Firstsec:
            clrf  TMR18          ; is 18. set TMR18 to zero and INC sec            
            incf  sec,f             ; also inc sectot (eventually lo and hi)

            incf  sectotlo,f      ; check sectot lo 00->inc hi
            clrw
           xorwf sectotlo,w   ; if equal, w= 0 and Z bit=1
            btfsc STATUS,2
            incf sectothi,f      ; increases every 256 x sectotlo
  
            movlw 0xA          ; check if =10 
           xorwf sec,w          ; if equal, w= 0 and Z bit=1
           btfss STATUS,2   ; test Z bit
           goto  restore        ; NOT 10 so pop stack and return to main 

            clrf sec               ; is 10. set sec to zero and inc sec10
            incf sec10,f        
            movlw 0x6         ; = 0x6
            xorwf sec10,w   ; if equal, w= 0 and Z bit=1
            btfss STATUS,2  ; test Z bit
            goto  restore      ; NOT 6 so pop stack and return to main 


            clrf  sec10         ; is 6. set sec10  to zero and INC min            
            incf  min,f
            movlw 0xA       ; check if =10 
            xorwf min,w     ; if equal, w= 0 and Z bit=1
            btfss STATUS,2  ; test Z bit
            goto  restore        ; NOT 10 so pop stack and return to main 

            clrf min                ; is 10. set sec to zero and inc min10
            incf min10,f        
            movlw 0x6          ;= 0x6
           xorwf min10,w     ; if equal, w= 0 and Z bit=1
           btfss STATUS,2  ; test Z bit
            goto  restore      ; NOT 6 so pop stack and return to main 

            clrf  min10         ; is 6. set min10  to zero and INC hr            
            incf  hr,f
            movlw 0xA        ;check if =10 
            xorwf hr,w         ; if equal, w= 0 and Z bit=1
            btfss STATUS,2  ; test Z bit
            goto  restore       ; NOT 10 so pop stack and return to main 

            clrf hr                   ; is 10. set hr to zero and inc hr10
            incf hr10,f        
            movlw 0xA          ;= 10
            xorwf hr10,w       ; if equal, w= 0 and Z bit=1
           btfss STATUS,2  ; test Z bit
            goto  restore      ; NOT 10 so pop stack and return to main   

                             
                             
            clrf sec              ; is ten (99hr 59min 59sec +1sec) we are at MAX
            clrf sec10          ;all reset to zero and start again
            clrf min
            clrf min10
            clrf hr
            clrf hr10
            clrf TMR18
            clrf oldsec 
            goto restore
 
restore: 
            swapf status_temp,W      ; get original status back
            movwf STATUS             ; into status register
            swapf fsr_temp,W         ; get original fsr back
            movwf FSR                  ; into status register
            swapf w_temp,f           ; old no flags trick again
            swapf w_temp,W         ; to restore W
            bcf INTCON,T0IF        ; clear the TMR0 interrupt flag
            retfie                           ; finished, reset GIE

;----------------------------------------------------------------------;
;                       Initialize the ports                           ;
;----------------------------------------------------------------------;
Init:
            clrf   PORTA
            clrf   PORTB

            movlw B'00000000'          ;  Porta all outputs
            tris PORTA
 
            movlw B'01010000'          ; 7o 6i-Wire 5o-LED 4i-multivib, 
            tris PORTB			   ; 0to3lcd


            movlw B'00000111'          ;  opt pull-ups enabled                                    
                                                     ;  opt prescaler assigned to TMR18
                                                     ;  opt prescaler set to 1:256
            option                               ;  opt rolls over each 125th second
                            
 
 movlw 0                                       ;  zero out all registers
 
    
 clrf m
 clrf m10
 clrf m100
 clrf m1000
 clrf dm
 clrf dm10
 clrf dm100
 clrf dm1000
 clrf dm10000
 clrf dm100000 

 clrf totmshi
 clrf totmslo
 clrf totms2hi
 clrf totms2lo
 clrf dist54
 
           
 clrf hr10
 clrf hr
 clrf min10                
 clrf min
 clrf oldsec
 clrf sec10
 clrf sec
 clrf sectotlo
 clrf dtotlo
 clrf dtothi
 clrf sectotlo
 clrf sectothi
 clrf TMR18   ; when this reaches 18, 1 sec has elapsed         
            
   
            ; TMR0 has to start at 256-217 :39, so that the first rollover
            ; with prescaler 256, happens in 217*256us =0.055552sec
            ; The TMR18 incs each roll and after 18  1 sec has elapsed
            movlw 0x27       ; D39  
            movwf TMR0    ; set to D39 217 counts later 256, 
                                     ; TMR0 resets and inc TMR18 in Isr loop

           clrf  INTCON      ; START WITH ALL AT 0
       
	                          ; movlw B'10100000'          ; GIE set T0IE set, T0IF cleared
          movlw B'10100000' ; Set:GIE,T0IE,RBIE Clrd:T0IF,RBIF
          movwf INTCON      ; ready to detect interrupts in ISR
          return

;----------------------------------------------------------------------;
;                 Initialize the LCD                                   ;
;----------------------------------------------------------------------;
Initlcd:
  movlw D'40'
  call   nmsec                  ;  Wait 40 msecs before Reset
  bcf    RS                       ;  send an 8 bit instruction
  movlw  0x03                 ;  Reset Command

  call   NybbleOut          ;  Send the Nybble
  call   Dlay5                  ;  Wait 5 msecs before Sending Again

  call EStrobe
  call Dlay160                ;  Wait 160 usecs before Sending 2nd Time
  call EStrobe
  call Dlay160                ;  Wait 160 usecs before Sending 3rd Time
  bcf    RS                     ;  send an 8 bit instruction
  movlw  0x02               ;  Set 4 Bit Mode

  call   NybbleOut              
  call Dlay160
  movlw  0x028                  ;  4 bit, 2 Line, 5x7 font
  call   SendINS
  movlw  0x010                  ;  display shift off
  call   SendINS
  movlw  0x001                  ;  Clear the Display RAM
  call   SendINS
  call   Dlay5                      ;  Note, Can take up to 4.1 msecs
  movlw  0x006                  ;  increment cursor
  call   SendINS
  movlw  0x00C                 ;  display on cursor off
  call   SendINS
  return

;----------------------------------------------------------------------;
;              Send the character in W out to the LCD                  ;
;----------------------------------------------------------------------;
SendASCII
  addlw '0'                     ;  Send nbr as ASCII character

SendCHAR:                  ;  Send the Character to the LCD
  movwf  temp               ;  Save the temporary Value
  swapf  temp, w           ;  Send the High Nybble
  bsf    RS                     ;  RS = 1
  call   NybbleOut
  movf   temp, w            ;  Send the Low Nybble
  bsf    RS
  call   NybbleOut
  return

;-------------------------------------------------------------
;              ES strobe
;-------------------------------------------------------------
EStrobe:                ;  Strobe the "E" Bit

  bsf    E
  bcf    E
  return
;----------------------------------------------------------------------;
;              Send an instruction in W out to the LCD                 ;
;----------------------------------------------------------------------;
SendINS:                  ;  Send the Instruction to the LCD
  movwf  temp           ;  Save w 
  swapf  temp, w       ;  send Hi Nybble
  bcf    RS                 ;  RS to 0
  call   NybbleOut
  movf   temp, w        ;  Send Lo Nybble
  bcf    RS
  call   NybbleOut
  return

;----------------------------------------------------------------------;
;              Send the nibble in W out to the LCD                     ;
;----------------------------------------------------------------------;
NybbleOut:              ;  Send a Nybble to the LCD
  movwf  PORTB         
  call EStrobe          ;  Strobe out the LCD Data
  bsf    E 
  call Dlay160          ;  delay for 160 usec
  return

;----------------------------------------------------------------------;
;                   Output the message on the LCD                      ;
;----------------------------------------------------------------------;
OutMessage1:
  movwf  FSR                    ;  Point at first letter
OutLoop:
  movf   FSR, w                 ;  Get pointer into W
  incf   FSR, f                     ;  Set up for next letter
  call   Dispmsg1               ;  Get character to output
  iorlw  0                            ;  At the End of the Message?
  btfsc  STATUS, Z            ;  Skip if not at end
  return                              ;  Yes - Equal to Zero
  call   SendCHAR             ;  Output the ASCII Character
  goto   OutLoop                ;  Get the next character

;----------------------------------------------------------------------;
;                    Data for message to be output                     ;
;----------------------------------------------------------------------;
Dispmsg1:                         ;  Message to Output
  addwf  PCL, f                  ;  Output the Characters
  dt     "Bike Computer", 0 

Dispmsg2:                       ;  Message to Output
  addwf  PCL, f                 ;  Output the Characters
  dt     "Dist", 0  

Dispmsg3: 
  addwf PCL,f                 ; message to output
  dt     " Av speed:",0      ; output characters

 ;----------------------------------------------------------------------;
 ;                        time delay routines                           ;
 ;----------------------------------------------------------------------;
;Note . The original application needed precise times from the  delay code that
 ; follows. But the Simple  Bike application does NOT use the routines for
 ; any CRITICAL time measurement.

Dlay160:    
  movlw D'41'                ;  delay about 160 usec

micro4:     
 addlw H'FF'                ;  subtract 1 from 'W'
 btfss STATUS,Z         ;  skip when you reach zero
 goto micro4                ;  more loops
 return                     

Dlay5:      
 movlw 5                     ;  delay for 5 milliseconds     
 goto $ + 2
msec250:    
 movlw D'250'              ; delay for 250 milliseconds

                            ; --- N millisecond delay routine ---
nmsec:      
 movwf cntmsec            ; delay for N (you put in W) millisec
msecloop:   
 movlw D'254'               ; load takes .9765625 microsec
 call micro4                  ; by itself CALL takes ...
                                    ; about 1ms
                                        
 nop                             ; 1usec 
 decfsz cntmsec, f       ; .98 skip not taken, else 1.95
 goto msecloop           ; 1.95 here: total ~1000 / loop
 return                         ; final time through ~999 to here
                                   ; overhead in and out ignored

                                  ; this block is functional for tests, but not used in bike
Dlay1sec:   	   ; this is a marker routine to see where
 call msec250             ; the prg is at.
; bsf  LED 
 call msec250          ;flash led on portb pin6
;bcf  LED 
 call msec250
; bsf  LED 
 call msec250	; finishes with green led lit
 return	

;----------------------------------------------------------------------;
;                       Display the Time                               ;
;----------------------------------------------------------------------;
Disptime:
   
 movlw H'80'        ;  position at beginning of first line
 call SendINS
 movf hr10, W      ;  tens of hours
 call SendASCII
 movf hr, W          ;  hours
 call SendASCII
 movlw ":"         
 call SendCHAR
 movf min10, W    ;  tens of minutes
 call SendASCII
 movf min, W        ;  minutes
 call SendASCII
 movlw ":"
 call SendCHAR
 movf sec10, W    ;  tens of seconds
 call SendASCII
 movf sec, W       ;  seconds
 call SendASCII
 movlw " "
 call SendCHAR   ; the h m s really not needed
 movlw "h"
 call SendCHAR
 movlw "m"
 call SendCHAR
 movlw "s"
 call SendCHAR
 movlw " "
 call SendCHAR

Dispend:
              RETURN
 
;----------------------------------------------------
;               End of display time
;----------------------------------------------------


;----------------------------------------------------------------------;
;                    Display the Distance , (trip distance at moment)                               ;
;----------------------------------------------------------------------;
Dispdist:       
 movlw H'C0'       ;  position at beginning of second line
 call SendINS

 movf dm100000, W     ;  0 of 065km 536m
 call SendASCII
 
 movf dm10000, W      ;  6 of 065km 536m
 call SendASCII

 
 movf dm1000, W       ;  5 of 065km 536m
 call SendASCII
 
 movlw "k"                 ;  k of 065km 536m
 call SendCHAR
 movlw "m"                ;  m of 065km 536m
 call SendCHAR
 movlw " "                  ;  " " of 065km 536m
 call SendCHAR

 movf dm100,W         ;  5 of 065km 536m
 call SendASCII

 movf dm10,W          ;  3 of 065km 536m
 call SendASCII
 movf dm, W             ;  6 of 065km 536m
 call SendASCII
             
 movlw "m"               ;  m of 065km 536m    
 call SendCHAR

 movlw " "                 ; " "  
 call SendCHAR
 movlw "D"               ; "D "  
 call SendCHAR
 movlw "i"                ; " i"  
 call SendCHAR
 movlw "s"               ; "s "  
 call SendCHAR
 movlw "t"                ; " t"  
 call SendCHAR
 movlw " "               ; " " ( to wipe screen) 
 call SendCHAR  
 
Distend:
              RETURN

  
; 
;---------------------------------------------------------------------*
;                Multiplication  16x16 Out32
;---------------------------------------------------------------------*
Mult16x16: 
	clrf mq4
	clrf mq3
	clrf mq2
	clrf mq1

  
	bsf mq2, 7
Mu1:
	rrf mah, f
	rrf mal, f
	skpc
	goto Mu2
	movf mbl, w
	addwf mq3, f
	movf mbh, w
	skpnc
	incfsz mbh, w
	addwf mq4, f
Mu2:
	rrf mq4, f
	rrf mq3, f
	rrf mq2, f
	rrf mq1, f
	skpc
	goto Mu1

Endmult16:
      clrf mbh
      clrf mbl
      clrf mah
      clrf mal

	Return

 
 	
 
;---------------------------------------------------------------------*
;            End Multiplication   16x16 Out32
;---------------------------------------------------------------------*

;---------------------------------------------------------------------*
;       New routine Division  16/16 Out 16
; ---------------------------------------------------------------------*
; Finally:- Result 000AH in reshi/lo, remainder 0096H in rmdrhi/lo
; so, checking end of division A=10 and 96H/E1H= 150/225 =0.666  OK

Div: 
       call    D_divS          ; remainder in Rmdr.
Divend:
   	  nop
    	  return
D_divS:
	call    setup
	clrf    rmdrhi
	clrf    rmdrlo
dloop:
      bcf     STATUS,C
	rlf     reslo, f
	rlf     reshi, f
	rlf     rmdrlo, f
	rlf     rmdrhi, f
	movf    denhi,w
	subwf   rmdrhi,w      ;check if a>c
	btfss   STATUS,Z
	goto    nochk
	movf    denlo,w
	subwf   rmdrlo,w      ;if msb equal then check lsb
nochk:
     	btfss   STATUS,C    ;carry set if c>a
	goto    nogo
	movf    denlo,w        ;c-a into c
	subwf   rmdrlo, f
	btfss   STATUS,C
	decf    rmdrhi, f
	movf    denhi,w
	subwf   rmdrhi, f
	bsf     STATUS,C    ;shift a 1 into b (result)
nogo:
                rlf     numlo,f
	rlf     numhi,f
	decfsz  temp, f        ;loop untill all bits checked
	goto    dloop
 
setup:
                movlw   .16             ; for 16 shifts
	movwf   temp
	movf    numhi,w      ;move Num to Res
	movwf   reshi
	movf    numlo,w
	movwf   reslo
	clrf    numhi
	clrf    numlo
	retlw   0
  
 ;--------------------------End Div 16 by 16----------------------------



	return
	
;---------------------------------------------------------------------*
; END DIVISION  16/16 OUT 16
;---------------------------------------------------------------

;---------------------------------------------------------------------*
; START Addition  16-16 OUT 16
;---------------------------------------------------------------
Add:  ; R = q1 + q2

	movf q1lo, W
	addwf q2lo, W
	movwf rlo

	movf q1hi, W
	btfsc STATUS, C
	addlw .1		; If A Carry Occurred, Add 1

	addwf q2hi, W
	movwf rhi
	;clrf q1hi                  ; added this to stop next user summing;
	;clrf q1lo
	;clrf q2hi
	;clrf q2lo
	
	Return

;---------------------------------------------------------------------*
; END Addition  16-16 OUT 16
;---------------------------------------------------------------



;---------------------------------------------------------------------*
; START Subtraction  16-16 OUT 16
;---------------------------------------------------------------
 

Sub:	; R = q1 - q2

	movf q2lo, W
	subwf q1lo, W	; W = q1lo - q2lo
	movwf rlo

	btfss STATUS, C
	Goto Borrow
	Goto Sub1
Borrow:
	Decf q1hi, F
	
Sub1:
	movf q2hi, W
	subwf q1hi, W	; W = q1hi - q2hi
	movwf rhi

	Return
 
;---------------------------------------------------------------
; END Subtraction  16-16 OUT 16
;---------------------------------------------------------------






;---------------------------------------------------------------
;                 Dist = counting wheel pulses 
;---------------------------------------------------------------

Dist:                ; whlcirc = 2.0747, for initial test =2
                      ;  
                      ; movlw 0x2	   ; <-- don't forget, this is just for 26"wheel
                      ; movwf whlcirc  ; start count at 0 and inc till same as whlcirc
                      ; (range will be 1 to 4 m)
   
  	 clrf count     ;  count can be 2 or 4 in this 26" case

Whloop:            ; although the distance pulses ( in this example) jump in steps of 2m
                         ; the m counter has to increase in 1's to catch the
	         ; decimal rollover from 10 to 0 
                         ; so count, here, is stepped from 0 to 2 (whlcirc)
                         ; Also, when the dist54 var increases 54m, COUNT adds an  extra 2m  
                         ; to the hex and decimal totals, before resetting to zero.
                ; Note that as dist54 is in the loop it will also receive 
	         ; the 2m increase so it  really  counts to 56 (54 +2).
  
 
       movlw 0x2               ; wheel circumference  of 26 inch
       xorwf count,w          ; if equal, w= 0 and Z bit=1
       btfsc STATUS,2      ; test Z bit
       goto DistanceEnd    ; they are the same, so jump distanceEnd and return  
       goto Mcount            ; NOT  yet = whlcirc, so inc mcount by 1 and  return  

Mcount:       ; Distance to decimal for display , i/p m, o/p 000km 000m 
  	    ; starts  with m, m10, m100, m1000 , m10000, m100000 = 0
 	    ; Later correct m every 54m (add 2m ???? )

      incf dm,f 
      movlw D'10'      
      xorwf dm,w           ; if equal, w= 0 and Z bit=1
      btfss STATUS,2   ; test Z bit
      goto Mcountend   ; NOT 0 so return
 
      clrf dm 
      incf dm10,f 
      movlw D'10'     
      xorwf dm10,w      ; if equal, w= 0 and Z bit=1
      btfss STATUS,2   ; test Z bit
      goto Mcountend   ; NOT 0 so return

      clrf dm10
      incf dm100,f 
      movlw D'10'      
      xorwf dm100,w     ; if equal, w= 0 and Z bit=1
      btfss STATUS,2   ; test Z bit
      goto Mcountend   ; NOT 0 so return

      clrf dm100
    incf dm1000,f 
      movlw D'10'         
      xorwf dm1000,w    ; if equal, w= 0 and Z bit=1
      btfss STATUS,2   ; test Z bit
      goto Mcountend   ; NOT 0 so return

      clrf dm1000
      incf dm10000,f 
      movlw D'10'       
      xorwf dm10000,w   ; if equal, w= 0 and Z bit=1
      btfss STATUS,2   ; test Z bit
      goto Mcountend   ; NOT 0 so return 
      
      clrf dm10000
      incf dm100000,f 
      movlw D'10'         
      xorwf dm100000,w  ; if equal, w= 0 and Z bit=1
      btfss STATUS,2      ; test Z bit
      goto Mcountend      ; NOT 0 so return 
      clrf dm                     ; is distmax, reset to 0	
      clrf dm10
      clrf dm100
      clrf dm1000
      clrf dm10000 
      clrf dm100000

      goto Mcountend  ; see if whpl  update is now finished



         
Mcountend:  ;update the hex counters dtot /lo/hi/hi2
      incf dtotlo,f
      clrw 
      xorwf dtotlo,w       ; if equal, w= 0 and Z bit=1
      btfss STATUS,2   ; test Z bit
      goto Dtotend        ; dtotlo NOT 0 
      incf dtothi,f           ; add 1, dtotlo is 0 
      clrw
      xorwf dtothi,w      ; if equal, w= 0 and Z bit=1
      btfss STATUS,2   ; test Z bit
      goto Dtotend        ; dtothi NOT 0 
      incf dtothi2,f         ; add 1, dtothi is 0 

Dtotend:
      movlw D'54'
      movwf numhi       ; not in use here, use to store 54
      movf  dist54,w			  
      subwf numhi,w
      btfsc STATUS,C      
      goto  Noextra      ; dist54=< 53, don't freeze COUNT
      movlw D'56'        ; dist54>53   add 2 extra counts
      movwf numhi      ; not in use here to store 56
      movf  dist54,w			  
      subwf numhi,w
      btfsc STATUS,C              
      goto  Extra          ; dist54=< 56, freeze COUNT
      clrf dist54
      goto Noextra



Noextra:
     incf dist54,f
     incf count,f 
    goto Whloop 

Extra:                          ; an extra loop BUT count is NOT increased 
    incf dist54,f 
   goto Whloop     

DistanceEnd:  ;  All conversion loops have executed and 
                       ; only left to display in decimal, when selected
		  ;  
       
  return          ; BACK TO MAIN
 
 
;--------------------------------------------------------------------------
;              End of Trip  Distance calculation Module
;--------------------------------------------------------------------------


;----------------------------------------------------------   
;     Calculate Average velocity from Velav = totdist/totsec		    
;-----------------------------------------------------------
; 1st time round distto = 0 and sectot =0.
; Disttot lo/hi zero is prob OKbut test sec=0. If zero return
Velav: 
 
     	 
     clrw
     xorwf sectotlo,w
     btfss STATUS,Z
     goto  Spdstart     ; not 0, onto Avspeed calc
     clrw 		 ; lo was 0, but hi byte may not be 
     xorwf sectothi,w
     btfss STATUS,Z     
     goto  Spdstart     ; was not 0, onto Avspeed calc
      ;incf  sectotlo,f   ; DUMMY
      ;goto  Spdstart   ; DUMMY
      return	  	 ; was also 0, abort module  wait for sec inc

		; to slow down the number of whpul in the test we add Dummies   
Spdstart:                 ; for test delay incf sec and sectot, gives 7.2km/hr
             
      
      ;incf  sec,f	  ; DUMMY
      ;incf sectotlo,f      ; DUMMY 
  
 
;Lpstart: 
      movlw 0x07  		 ; 1800   comp hi bytes
      subwf dtothi,w
      btfsc STATUS,C      
      goto  Divratio      ;  dtothi is > 1800hi and needs dividing
	                 ;  we suppose that the hibyte test is sufficient,
		 ;  it may not be true. e.g hi's the same but lo's diff
                                ;  pass dtot to m1000,m100
    movf dtothi,w        ; dtothi <1800
    movwf m1000
    movf dtotlo,w
    movwf m100         ; m1000,m100 hold dtothi/lo ( no div needed)
    movf  sectothi,w    
    movwf m10
    movf sectotlo,w
    movwf m             ; m10,m hold sectothi/lo ( no div needed)   

	               ; note, this is dtot to m1000,m100 not needing division
    goto  Ratend       ; if divratio is needed, then m1000,m100 is used there 
 
Divratio:                   ; enter variables and 1800

      movf  dtothi,w
      movwf numhi 
      movf  dtotlo,w
      movwf numlo
      movlw 0x07
      movwf denhi
      movlw 0x08
      movwf denlo
      call  Div
      incf  reslo,w    ; reslo holds INT of Divratio +1
      movwf count   ; not used at the moment 
      movf  dtothi,w ; divide dtothi/lo by count
      movwf numhi 
      movf  dtotlo,w
      movwf numlo
      clrf  denhi 
      movf  count,w  ; e.g. D 37
      movwf denlo
      call  Div          ; we need parking space for results, REuse the m1000 etc
      movf  reshi,w   
      movwf m1000 ; 
      movf  reslo,w
      movwf m100   ; m1000, m100 =dtot/count
                            ; repeat for sectot
      movf  sectothi,w
      movwf numhi 
      movf  sectotlo,w
      movwf numlo
      clrf  denhi 
      movf  count,w    ; e.g D 37
      movwf denlo
      call  Div
      movf  reshi,w
      movwf m10  
      movf  reslo,w
      movwf m         ; m10, m =sectot/count
Ratend:

 ;    WE CAN NOW DO DTOT/SECTOT x3.6  as we have SCALED TO AVOID OVERFLOW

 ;                m1000,m100   x 36   (top line)
 ;     Vav=   -------------------------                      = Average speed in km/hr

 ;               m10,m        x 10   (bottom line)         ;     36(24H), 10(AH) 

 

      movf m1000,w     ; MULT dtot, top line,  by 24H
      movwf mah
      movf m100,w
      movwf mal
      clrf  mbh
      movlw 0x24
      movwf mbl
      call  Mult16x16  ; ( store top line result in the same variables)
      movf  mq2,w
      movwf m1000 
      movf  mq1,w
      movwf m100    ;  dtot x 36
 
     movf m10,w      ; MULT sectot, bottom line,  by AH
     movwf mah
     movf m,w
     movwf mal
      clrf  mbh
      movlw 0xA
      movwf mbl
      call  Mult16x16 ; ( store bottom line result in the same variables)
      movf  mq2,w
      movwf m10 
      movf  mq1,w
      movwf m         ;  sectot x 10
Tp3:

       ; As we are reusing variables, trying to show where the results to here are being held
 ;			                m1000,m100	
 ;   We now have as  Average speed     ----- ---------       km/hr
 ;                                                              m10,m
 

   ;the AVERAGE SPEED AT LAST

      movf  m1000,w  ; dtotx36/sectotx10
      movwf numhi 
      movf  m100,w  
      movwf numlo
      movf  m10,w
      movwf denhi    ; needed below for decimal place
     movf  m,w
     movwf denlo    ; needed below for decimal place 
     call  Div
                             ; denhi/lo can chanfge as the div routine reuses bits
	            ; so we reestablish previous denom values before losing
    movf  m10,w
    movwf denhi
    movf  m,w
    movwf denlo   ; we can now write over m and m10  
            		
    movf  reshi,w  ;will be zero
 	           ;(bikes don't exceed 256km/hr normally) 
    movf  reslo,w  ; reuse variables m10 INT and later m FRAC
    movwf m10     ;we have integer part of Vav Average speed
                    
   
 
                         ; rmdrhi/lo holds decimal fraction.we will only use 1 decimal place.  
 	        ; so mult the rmdrhi,lo x10  before dividing by denhi/lo
       

  movf  rmdrhi,w     ; mult top by 10 
  movwf mah
  movf  rmdrlo,w
  movwf mal           ; rmdr in tot storage
  clrf  mbh
  movlw 0xA
  movwf mbl
  call  Mult16x16  ; rmdrhi,lo x 10
                            ; top line is now  mq2,mq1 ( nothing in mq4,mq3)  
                            ; Bottom line..remember we already have loaded
                           ; the denominator above which says  
                            ; " needed below for decimal place"  
Tp4:
movf mq2,w       ;rmdrhi,w      
    movwf numhi
    movf  mq1,w        ;rmdrlo,w     
    movwf numlo	      
    call Div                ; div for the decimal( just 1 place), in reslo
      		; reshi,reslo holds decimal place results
                                ; m10+m are  av speed as INT+DEC but in hex
                               ; now change to decimal.
                               ; m10 and m are going to be wiped, but there is no
                               ;divide routine so for INT +FRAC we use reshi reslo 
      movf  m10,w
      movwf reshi      ; OK? yes reshi +reslo = INT+FRAC in hex, convert	 
        
 
Vavh2d:

     clrf m
     clrf m10
    clrf count

Vavloh2d:                ; m is 2nd dec, use m10
       movf  count,w
       xorwf reslo,w 
       btfsc STATUS,Z 
       goto  Vavloend                
       incf  count,f
	 incf  m10,f
       movlw 0xA         
       xorwf m10,w
       btfss STATUS,Z
       goto Vavloh2d
       clrf m10
       incf m,f
       goto Vavloh2d

Vavloend:

      clrf m100
      clrf m1000
      clrf count

Vavhih2d:
       movf  count,w
       xorwf reshi,w
       btfsc STATUS,Z
       goto  Vavhiend                 
       incf  count,f
       incf  m100,f
       movlw 0xA         
       xorwf m100,w
       btfss STATUS,Z
       goto Vavhih2d
       clrf m100
       incf m1000,f
       goto Vavhih2d

Vavhiend:                 ; We have Vav as: INT m1000,m100 and FRAC  m10,m with Frac
       
       
 
Vavend:
         nop
         return


;----------------------------------------------------------------------;
;                    Display the Average velocity                               ;
;----------------------------------------------------------------------;
Dispvav:
        
 movlw H'C0'       ;  position at beginning of second line
 call SendINS
 
 

 movf m1000, W     ;    x0 tens of km
 call SendASCII
  

 movf m100,W         ;   0x ones of km
 call SendASCII

 movlw "."                 ;  "." punto decimal  
 call SendCHAR

 movf m10,W             ;  "0.x0" decimal
 call SendASCII
 movf m, W              ;  "0.0x" decimal
 call SendASCII
             
 movlw "k"                   ;  k  
 call SendCHAR
 movlw "m"                 ;  m  
 call SendCHAR

 movlw "/"                   ;  /
 call SendCHAR

 movlw "h"                  ;  h
 call SendCHAR
 movlw "r"                   ;  r
 call SendCHAR

 movlw " "                   ;  " "
 call SendCHAR

 movlw "V"                ;  V
 call SendCHAR
 movlw "e"                  ;  e
 call SendCHAR 

 movlw "l"                  ;  l
 call SendCHAR
 movlw "A"                 ;  A
 call SendCHAR
 

 movlw "v"                 ;  v
 call SendCHAR
 movlw "g"                 ;  g
 call SendCHAR 
 movlw " "                   ;  " "
 call SendCHAR
  
 
Dispvavend:
               
  Return                    ; to change seconds for next second
      
;--------------END OF AV SPEED-------------------







   


; ---------------------------------------------------------------       
;                   Instantaneous Speed  
;-----------------------------------------------------------------       
 ;  Previous time values  are subtracted from   the present
 ;  sec, TMR18, TMR0 values. 
 ;  This time difference is the time between
 ; wheel pulses. This in turn for 26" wheel is a distance of 2.0747m
 ; is covered in totms
; ---------------------------------------------------------------       
;                   Instantaneous Speed  
;-----------------------------------------------------------------       
; Note sec can only move between  0 and 9, 10 is a new 0 
; We start by reading sec, TMR18, TMR0 TO GET START TIME

Speed:
 
			
Spdflg0:                  ; FIRST TIMEwith spdflg=0, second time with spdflg=1
                              ; First time captures timezero 0 in ms in totms
	clrw
	xorwf spdflg,w
	btfss STATUS ,Z
	goto Spdflg1    ; spdflg is not 0, this is the second time
      call Summs
      movf  mshi,w
      movwf totmshi  
      movf  mslo,w
      movwf totmslo
      incf spdflg,f  ; (spdflg = 1 for  SECOND TIME)
      return   ;jump back to main menu
      
Summs:   ; this routine is common to the first and second time
      movf sec,w     ; spdflg = 0
      movwf mbl      ; sec can only be in range 0 to 9 
      clrf  mbh
      movlw 0x03		; 1000 = 03E8
      movwf mah  
      movlw 0xE8		; 1000 = 03E8
      movwf mal  
	call  Mult16x16 ; 
      movf  mq2,w    ; hi byte
      movwf mshi 
      movf  mq1,w    ; lo byte
      movwf mslo
      
	movf  TMR18,w
	movwf mal
	clrf  mah 
      movlw D'56'
      movwf mbl
      clrf  mbh
	call  Mult16x16 ;  after themult we add
	movf  mshi,w
        movwf q1hi     
	movf  mslo,w
        movwf q1lo    
	movf  mq2,w
        movwf q2hi     
	movf  mq1,w
        movwf q2lo    
        call  Add
	movf  rhi,w
        movwf mshi
	movf  rlo,w
        movwf mslo   ; sum  (sec +TMR18)
                         ;TMR0 is a little more complex as we have to subtract39
                         ; before we can calculate ms in TMR0
        movf  TMR0,w
        movwf q1lo
        clrf  q1hi 
        movlw  D'39'
        movwf q2lo
        clrf q2hi
        call Sub
        movf rlo, w
        movwf numlo
        clrf  numhi
        movlw 0x4
        movwf denlo
        clrf  denhi
	call  Div

	movf  mshi,w
        movwf q1hi     
	movf  mslo,w
        movwf q1lo    
	movf  reshi,w
        movwf q2hi     
	movf  reslo,w
        movwf q2lo    
        call  Add   
        movf  rhi,w
        movwf mshi
        movf  rlo,w
        movwf mslo ;   ( SEC +TMR18+ TMR0) in ms mshi/lo

        RETURN     ; Summs
Summsend:

 


Spdflg1: ; SECOND TIME is with spdflg =1
		         ; Second time captures timezero to  2nd Pulse 
                     ; in ms, store  in totms2
                     ; for test we add 250ms to simulate time between
                     ; wheel pulses
                     ; 250ms delay
      ;movlw D'63'   ; DUMMY FOR TEST
      ;call nmsec     ; DUMMY FOR TEST             

 	call Summs

      movf  mshi,w   ; 
      movwf totms2hi ;  
      movf  mslo,w    ;  
      movwf totms2lo  ; 


; totms2 should be larger than totms,however if totms2 has just rolled,
; then add 10000 to tot2 

Testtot2:
       movf  totms2hi,w
       subwf totmshi,w
       btfss STATUS,C   ;compare for greater
       goto  Diffms     ;totms2 > totms  no add 10000 needed
       movlw 0xA        ;totms2 < totms, add 10000, hex 2710
 	 movlw 0x27
       movwf q1hi
 	 movlw 0x10
       movwf q1lo 
       movf totms2hi,w
	 movwf q2hi   
       movf totms2lo,w
	 movwf q2lo
       call  Add     ; tot2 +10000
	 movf  rhi,w
       movwf totms2hi
	 movf  rlo,w
       movwf totms2lo  ; tot2 = tot2+10000


               
Diffms: 


     ; sub  (tot2-tot1)for time between wheel pulses

	movf   totms2hi,w  ;
      movwf  q1hi  
	movf   totms2lo,w
      movwf  q1lo      

	movf   totmshi,w
      movwf  q2hi  
	movf   totmslo,w
      movwf  q2lo     
      call   Sub  ; result is  time diff, stored in rhi, rlo
tp2:
   ; we are almost ready to divide dist/time for speed
   ; complete equation is dist/time x3.6
   ; to avoid as far as possible fractions we multiply before divide
   ; but this time, better divide first.

   ; 2000            36        <--- this is more than 65536
   ; ----       x    --------
   ; denhi/lo         10    ( denhi/lo is sub result rhi/lo)

   ; so we can divide by 10 and x by 36 ( always,)
   ; so top  line is simply 7200.
   ; we finally have 7200/(denhi/lo) , SPEED IN HEX


      movf   rhi,w
	movwf  denhi
      movf   rlo,w
	movwf  denlo  ; denom is time in hex
      movlw  0x1C   ; 7200, hex 1C20
      movwf  numhi
      movlw  0x20   ; 7200, hex 1C20
      movwf  numlo 
	call Div 
                    ; the result , res, is  more than 1 
		        ;and a remainder rmdr/den
                    ;we now have 6 free variables
                    ; mshi/lo  for INT hex speed lo is enough
		        ; totmshi/lo
		        ; totms2hi/lo    
                    ;we will reuse to convert from hex to decimal

	movf  reslo,w 
      movwf mshi   ; INT part of hex speed ( hi, because FRAC will be in lo)

                    ;we will only use 1 decimal of 
		        ;the hex speed remainder, so 1st  rmdrx10  
                    ;  so next calculation is

			  ; 10 x rmdrhi/lo
                    ; ---------------
	              ; denhi/lo 

      movf rmdrhi,w
      movwf mah
      movf rmdrlo,w
      movwf mal
	clrf  mbh
	movlw 0x0A
	movwf mbl
      call  Mult16x16  ;( ten times top)  
      movf  mq2,w
      movwf numhi 
      movf  mq1,w
      movwf numlo  ; the den is previously loaded so divide for 
                   ; decimal part of speed ( still in hex)
	call  Div    ; we only use 1 decimal, so just use lo of Div result
      movf  reslo,w
      movwf mslo    ; FRAC  of speed in hex
                   ; now convert speed to decimal form
                   ; i.e. mshi, mslo --> 99.9 km/hr

Spdh2d:

	clrf m
	clrf m10
	clrf m100
	clrf m1000
	clrf count


Spdlo:
       movf  count,w
       xorwf mslo,w 
       btfsc STATUS,Z 
       goto  Spdhicnt                
       incf  count,f
	 incf  m10,f
       movlw 0xA         
       xorwf m10,w
       btfss STATUS,Z
       goto Spdlo 
       clrf m10
       incf m,f
       goto Spdlo 

Spdhicnt:
	clrf m100    
	clrf m1000
	clrf count

Spdhi:
       movf  count,w
       xorwf mshi,w
       btfsc STATUS,Z
       goto  Spdhiend                 
       incf  count,f
	 incf  m100,f
       movlw 0xA         
       xorwf m100,w
       btfss STATUS,Z
       goto Spdhi 
       clrf m100
       incf m1000,f
       goto Spdhi 

Spdhiend:; We have filled m1000,m100 with INT and m10,m with Frac, of Vin.
         ; Note we only need m10 ( 1 decimal place)
      clrf spdflg ; reset to 0 for next pair of pulses







     ; incf spdflg,f  ; (spdflg = 1 for  SECOND TIME)

 	return
; ---------------------------------------------------------------**
;                         End of Instantaneous Speed
; ---------------------------------------------------------------** 



Dispd:
;----------------------------------------------------------------------;
;                    Display the Instantaneous Speed (Speed)                               ;
;----------------------------------------------------------------------;
        
 movlw H'C0'       ;  position at beginning of second line
 call SendINS
 


 movf m1000, W     ;    x0 tens of km
 call SendASCII
  

 movf m100,W         ;   0x ones of km
 call SendASCII

 movlw "."         ;  "." punto decimal  
 call SendCHAR

 movf m10,W     ;  "0.x0" decimal
 call SendASCII
 movf m, W      ;  "0.0x" decimal
 call SendASCII
             
 movlw "k"         ;  k  
 call SendCHAR
 movlw "m"         ;  m  
 call SendCHAR

 movlw "/"         ;  /
 call SendCHAR

 movlw "h"         ;  h
 call SendCHAR
 movlw "r"         ;  r
 call SendCHAR
 

 movlw " "         ;  " "
 call SendCHAR

 movlw "S"         ;  S
 call SendCHAR
 movlw "p"         ;  p
 call SendCHAR 

 movlw "e"         ;  e
 call SendCHAR
 movlw "e"         ;  e
 call SendCHAR
 

 movlw "d"         ;  d
 call SendCHAR
  
Dispdend:
               
  Return    ; to change seconds for next second
      
;--------------END OF Inst SPEED-------------------
 
     end 
;-----------------------------------------------------
;             bike computer program end  ....enjoy
;-----------------------------------------------------



;                    WORK IN HAND

; CLEAN UP  SPEED,  readout still a little jumpy, 
; can try integrating but need more space. Next version

;           Coventions used in the program text. 
 ;Variables all small letters , exceptions, those defined 
 ; by microchip. e.g. STATUS
 ;Labels: First letter always a Capital, exceptions imported
 ; routines
 ; Test points. These have  a comment DUMMY, the value of the code
 ; has/had been changed for testing or simulating.  If YOU want 
 ;to polish  these points should be useful for value changes
 ; example. In ISR the D'18' value to reach a sec is very slow.
 ;Use for test , say 2, and you get going about 10 times  faster.
 ;Also LCD INIT: can be bypassed to debug quicker


;----------------------------------------------------------------------------------------------------
;                   End of the Simple Bike Computer
;----------------------------------------------------------------------------------------------------


The END Product the HEX file

  If you have managed to read to here, through all that has been written, then you certainly have determination.

Well “ getting here” is the moment when you have mastered all the steps in writing the PIC assembler source code. Getting here also means you can do all the manual side as well for many future projects.
But maybe the most satisfaction is obtained by taking the final code version
and then burning it into the PIC to make your Bike Computer a reality,….not just a bit of theory.

Ah I almost forgot, here is the Bike3.asm squeezed into the END PRODUCT of our toil.

Bike3. hex

:020000000528D1
:080008004C289220BD20CB0121
:10001000C80138083206031909283208B800CB0A8B
:100020002E2186018612061A132886160A302221EE
:10003000061A18280A302221061A182886120608DD
:100040000A304B02031C25283028CB220A304B02F1
:10005000031C2B20302803014806031970230800D5
:10006000C4210A304B02031C392814304B02031CF4
:100070004B21122214304B02031C42281E304B022B
:10008000031CA4221F304B06031D4728CB01380850
:100090003206031911280928BB00030EBC00040E08
:1000A000BD0027308100BA0A12303A06031D8A28A3
:1000B000BA01B20AB00A030130060319B10A0A30C4
:1000C0003206031D8A28B201B30A06303306031D27
:1000D0008A28B301B40A0A303406031D8A28B40101
:1000E000B50A06303506031D8A28B501B60A0A305E
:1000F0003606031D8A28B601B70A0A303706031DE3
:100100008A28B201B301B401B501B601B701BA0141
:10011000B8018A283C0E83003D0E8400BB0E3B0EC6
:100120000B11090085018601003065005030660022
:10013000073062000030AC01AD01AE01AF01A60195
:10014000A701A801A901AA01AB01C301C201C50110
:10015000C401A501B701B601B501B401B801B301ED
:10016000B201B001A201A301B001B101BA0127306F
:1001700081008B01A0308B0008002830222105115E
:100180000330EB201F21E0201A21E0201A21051165
:100190000230EB201A212830E3201030E320013018
:1001A000E3201F210630E3200C30E3200800303E1E
:1001B000C900490E0515EB2049080515EB2008007C
:1001C000851585110800C900490E0511EB20490865
:1001D0000511EB2008008600E02085151A21080093
:1001E00084000408840AF920003803190800D82084
:1001F000F1288207423469346B3465342034433447
:100200006F346D3470347534743465347234003442
:1002100082074434693473347434003482072034E0
:100220004134763420347334703465346534643446
:100230003A3400342930FF3E031D1B2908000530E5
:100240002229FA30B900FE301B210000B90B232906
:100250000800212121212121212108008030E320D3
:100260003708D7203608D7203A30D8203508D7208D
:100270003408D7203A30D8203308D7203208D72086
:100280002030D8206830D8206D30D8207330D82066
:100290002030D8200800C030E3202B08D7202A08BF
:1002A000D7202908D7206B30D8206D30D8202030B7
:1002B000D8202808D7202708D7202608D7206D3037
:1002C000D8202030D8204430D8206930D82073304E
:1002D000D8207430D8202030D82008008C018D011F
:1002E0008E018F018E17920C930C031C7D2911082F
:1002F0008D0710080318100F8C078C0C8D0C8E0CBA
:100300008F0C031C73299001910192019301080045
:100310008B2100000800A6219901980103109B0D74
:100320009A0D980D990D15081902031D99291408A5
:100330001802031CA22914089802031C990315082B
:1003400099020314960D970DC90B8E291030C90020
:1003500017089A0016089B009701960100340800C0
:100360001D081F07A1001C080318013E1E07A0005E
:1003700008001F081D02A100031CBF29C0299C03FF
:100380001E081C02A0000800CA0102304A06031918
:10039000112AA60A0A302606031DF329A601A70A78
:1003A0000A302706031DF329A701A80A0A302806E8
:1003B000031DF329A801A90A0A302906031DF32900
:1003C000A901AA0A0A302A06031DF329AA01AB0AC9
:1003D0000A302B06031DF329A601A701A801A901D4
:1003E000AA01AB01F329A20A03012206031DFE297B
:1003F000A30A03012306031DFE29A40A3630970031
:100400002508170203180C2A383097002508170210
:1004100003180F2AA5010C2AA50ACA0AC529A50A8C
:10042000C529080003013006031D1B2A03013106FC
:10043000031D1B2A0800073023020318282A23085B
:10044000AF002208AE003108AD003008AC004B2AE6
:100450002308970022089600073095000830940082
:1004600088211B0ACA0023089700220896009501DC
:100470004A08940088211A08AF001B08AE00310812
:1004800097003008960095014A08940088211A08C0
:10049000AD001B08AC002F0892002E0893009001BD
:1004A000243091006E210E08AF000F08AE002D0819
:1004B00092002C08930090010A3091006E210E08E2
:1004C000AD000F08AC002F0897002E0896002D08ED
:1004D00095002C08940088212D0895002C08940084
:1004E0001A081B08AD001908920018089300900123
:1004F0000A3091006E210E0897000F08960088219F
:100500002D089A00AC01AD01CA014A081B06031967
:10051000922ACA0AAD0A0A302D06031D852AAD01AA
:10052000AC0A852AAE01AF01CA014A081A060319AE
:10053000A22ACA0AAE0A0A302E06031D952AAE0167
:10054000AF0A952A00000800C030E3202F08D7200A
:100550002E08D7202E30D8202D08D7202C08D720C1
:100560006B30D8206D30D8202F30D8206830D8207C
:100570007230D8202030D8205630D8206530D8208E
:100580006C30D8204130D8207630D8206730D82041
:100590002030D820080003014806031D122BD62264
:1005A0004608C3004708C200C80A08003208910084
:1005B000900103309200E83093006E210E08C600CF
:1005C0000F08C7003A08930092013830910090015B
:1005D0006E2146089C0047089D000E089E000F08EB
:1005E0009F00B0212008C6002108C70001089D0017
:1005F0009C0127309F009E01B92121089600970198
:10060000043094009501882146089C0047089D000D
:100610001A089E001B089F00B0212008C600210870
:10062000C7000800D6224608C5004708C400450890
:100630004302031C292B0A3027309C0010309D00F8
:1006400045089E0044089F00B0212008C5002108ED
:10065000C40045089C0044089D0043089E004208D1
:100660009F00B92120089500210894001C309700B4
:100670002030960088211B08C6001908920018082F
:10068000930090010A3091006E210E0897000F0828
:10069000960088211B08C700AC01AD01AE01AF0177
:1006A000CA014A08470603195E2BCA0AAD0A0A3076
:1006B0002D06031D512BAD01AC0A512BAE01AF012C
:1006C000CA014A08460603196E2BCA0AAE0A0A3046
:1006D0002E06031D612BAE01AF0A612BC801080075
:1006E000C030E3202F08D7202E08D7202E30D82066
:1006F0002D08D7202C08D7206B30D8206D30D8207B
:100700002F30D8206830D8207230D8202030D82020
:100710005330D8207030D8206530D8206530D820AC
:060720006430D82008003F
:02400E00F13F80
:00000001FF



CONCLUSION



Just a final word about the the skills we have learned up to here-

We said in the introduction, that most people maintain the hardest part of working with ucontrollers is writing the source code.

So we decided to start with the “easy” part, the hardware.

However after learning how to make our own PCB maybe we don't agree.
The people who say the hardware is EASY, either lead a very secluded life (someome else does the layout, makes the PCB etc ), or perhaps they have never even thought that such things exist.
Then the source code side is the easier? No, simply it is different activity which can be as formidable as the hardware integration we have learned.

A final thought. Computers get faster and faster,... thanks to ?
Yes, you guessed it.. thanks to ongoing hardware improvements.
The factor which limits the computer today 2002 is the need to connect the bits together inside the computer box. It is now very important to consider and design the PCB, in the high speed parts, using microstrip technology.