;;;;;;; P5 for QwikFlash board ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
; Name: Shane Connelly
; Name: Grant Lohsen
; Handle: WSCL
;
; PIC18F452's internal clock period is 0.4 microseconds.
; Use Timer2 for a loop time of approximately ten milliseconds (see Figure 16-3)
; with A=16, B=223, C=7 for 24976 internal clock cycles = 9990.4 microseconds.
; Toggle B0 output every ten milliseconds for measuring looptime with scope.
; Step stepper motor at a rate of 100 steps per second.
; Blink "Alive" LED every two seconds.
; Also, the code reads the value of the potentiometer every 1 second
; After it reads the potentiometer, it sets the stepper motor to step n times per
; second where n = 12 * (the potentiometer value from 0-8). You may enter the number
; of steps to take with the keypad, as well as the direction to turn (*: clockwise,
; #: counterclockwise).
;
;;;;;;; Program hierarchy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Mainline
;   Initial
;   PotDisplay
;     ReadPot
;     TXbyte
;     RateDisplay
;       FXD0808U
;       TXbyte
;   Keypad
;     ReadKeypad
;     SendStrg
;       TXbyte
;     ResetKeypad
;   ControlStepping
;   BlinkAlive
;   LoopTime
;
;;;;;;; Assembler directives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        list  P=PIC18F452, F=INHX32, C=160, N=0, ST=OFF, MM=OFF, R=DEC, X=ON
        #include P18F452.inc
        __CONFIG  _CONFIG1H, _HS_OSC_1H  ;HS oscillator
        __CONFIG  _CONFIG2L, _PWRT_ON_2L & _BOR_ON_2L & _BORV_42_2L  ;Reset
        __CONFIG  _CONFIG2H, _WDT_OFF_2H  ;Watchdog timer disabled
        __CONFIG  _CONFIG3H, _CCP2MX_ON_3H  ;CCP2 to RC1 (rather than to RB3)
        __CONFIG  _CONFIG4L, _LVP_OFF_4L  ;RB5 enabled for I/O

;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        cblock  0x000           ;Beginning of Access RAM
        COUNT
        ALIVECNT                ;Counter for blinking "Alive" LED
        HUNDRED                 ;Counter for reading the potentiometer value
        POTVALUE                ;Holds the raw potentiometer value
        SMALLPOT                ;Holds the scaled value of the potentiometer
        STEPRATE                ;The rate in steps/second of the stepper motor
        ACCUM                   ;The accumulator for the motor
        NUML                    ;Low byte of number entered
        NUMH                    ;High byte of number entered
        NUMSTPSL                ;Low byte of NUMSTPS and the number of steps to take
        NUMSTPSH                ;High byte of numstps
        POSH                    ;High byte of the stepper motor position
        POSL                    ;Low byte of the stepper motor position
        DISP_POS:7              ;Display information for stepper motor position
        DISP_RATE:4             ;Display information for stepper motor rate
        DISP_BUF:6              ;Display information for numerical input
        KEYSTRG:4               ;A 4-byte string to hold digit keycodes entered in succession
        FLAGS                   ;The bits of flags are used as state bits
        DISPLAYNUMH             ;High byte of the number to be displayed on the LCD
        DISPLAYNUML             ;Low byte of the number to be displayed on the LCD
        DISPLAYPOS              ;Position of the LCD display number in RAM
        DISPLAYLEN              ;Length of the LCD display number in RAM
        DISPLAYSIGN             ;Sign of the LCD display
        endc
        
        #include <C:\MATH18\MATHVARS.inc>

;;;;;;; Macro definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

MOVLF   macro  literal,dest
        movlw  literal
        movwf  dest
        endm
        
POINT   macro  stringname
        MOVLF  high stringname, TBLPTRH
        MOVLF  low stringname, TBLPTRL
        endm

;;;;;;; Vectors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        org  0x0000             ;Reset vector
        goto  Mainline

        org  0x0008             ;High priority interrupt vector
        goto  $                 ;Trap

        org  0x0018             ;Low priority interrupt vector
        goto  $                 ;Trap

;;;;;;; Mainline program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Mainline
        call  Initial           ;Initialize everything
        LOOP_
          btg  PORTB,RB0        ;Toggle pin, to support measuring loop time
          bsf PORTC, RC2        ;Toggle pin to support measuring useful function time
          ;call  ResetKeypad
          call  PotDisplay      ;run the potentiometer read and display functions
          call  Keypad
          call  ControlStepping ;control the motor step rate
          call  BlinkAlive      ;Blink "Alive" LED
          bcf PORTC, RC2        ;set the pin low to end timing
          call  LoopTime        ;Wait for ten milliseconds to be up
        ENDLOOP_

;;;;;;; Initial subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine performs all initializations of variables and registers.

Initial
        MOVLF  B'01001110',ADCON1    ;Enable PORTA & PORTE digital I/O pins
        MOVLF  B'01100001',ADCON0    ;Select pot input to ADC
        MOVLF  B'11100001',TRISA    ;Set I/O for PORTA
        MOVLF  B'11001100',TRISB    ;Set I/O for PORTB
        MOVLF  B'11010000',TRISC    ;Set I/O for PORTC
        MOVLF  B'00001111',TRISD    ;Set I/O for PORTD
        MOVLF  B'00000100',TRISE    ;Set I/O for PORTE
        MOVLF  B'00010000',PORTA    ;Turn off all four LEDs driven from PORTA
        bsf  PORTB,RB5            ;Step direction is CW
        MOVLF  B'10110111',T2CON    ;Set up Timer2 (10 millisecond looptime)
        MOVLF  222, PR2
        MOVLF 200,ALIVECNT        ;set the time/loop
        MOVLF 100,HUNDRED         ;set the time/loop
        call  InitLCD
        return

;;;;;;; InitLCD subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Initialize the Optrex 8x2 character LCD.
; First wait for 0.1 second, to get past display's power-on reset time.

InitLCD
        MOVLF  10,COUNT         ;Wait 0.1 second
        REPEAT_
          rcall  LoopTime       ;Call LoopTime 10 times
          decf  COUNT,F
        UNTIL_  .Z.

        bcf  PORTE,0            ;RS=0 for command
        POINT  LCDstr           ;Set up table pointer to initialization string
        tblrd*                  ;Get first byte from string into TABLAT
        REPEAT_
          bsf  PORTE,1          ;Drive E high
          movff  TABLAT,PORTD   ;Send upper nibble
          bcf  PORTE,1          ;Drive E low so LCD will process input
          rcall  LoopTime       ;Wait ten milliseconds
          bsf  PORTE,1          ;Drive E high
          swapf  TABLAT,W       ;Swap nibbles
          movwf  PORTD          ;Send lower nibble
          bcf  PORTE,1          ;Drive E low so LCD will process input
          rcall  LoopTime       ;Wait ten milliseconds
          tblrd+*               ;Increment pointer and get next byte
          movf  TABLAT,F        ;Is it zero?
        UNTIL_  .Z.
        return
        
;;;;;;;;DisplayC subroutine;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine is called with TBLPTR containing the address of a constant
; display string.  It sends the bytes of the string to the LCD.  The first
; byte sets the cursor position.  The remaining bytes are displayed, beginning
; at that position.
; This subroutine expects a normal one-byte cursor-positioning code, 0xhh, or
; an occasionally used two-byte cursor-positioning code of the form 0x00hh.

DisplayC
        bcf  PORTE,0            ;Drive RS pin low for cursor-positioning code
        tblrd*                  ;Get byte from string into TABLAT
        movf  TABLAT,F          ;Check for leading zero byte
        IF_  .Z.
          tblrd+*               ;If zero, get next byte
        ENDIF_
        REPEAT_
          bsf  PORTE,1          ;Drive E pin high
          movff  TABLAT,PORTD   ;Send upper nibble
          bcf  PORTE,1          ;Drive E pin low so LCD will accept nibble
          bsf  PORTE,1          ;Drive E pin high again
          swapf  TABLAT,W       ;Swap nibbles
          movwf  PORTD          ;Write lower nibble
          bcf  PORTE,1          ;Drive E pin low so LCD will process byte
          rcall  T40            ;Wait 40 usec
          bsf  PORTE,0          ;Drive RS pin high for displayable characters
          tblrd+*               ;Increment pointer, then get next byte
          movf  TABLAT,F        ;Is it zero?
        UNTIL_  .Z.
       return
       
;;;;;;; DisplayV subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine is called with FSR0 containing the address of a variable
; display string.  It sends the bytes of the string to the LCD.  The first
; byte sets the cursor position.  The remaining bytes are displayed, beginning
; at that position.

DisplayV
        bcf  PORTE,0            ;Drive RS pin low for cursor positioning code
        REPEAT_
          bsf  PORTE,1          ;Drive E pin high
          movff  INDF0,PORTD    ;Send upper nibble
          bcf  PORTE,1          ;Drive E pin low so LCD will accept nibble
          bsf  PORTE,1          ;Drive E pin high again
          swapf  INDF0,W        ;Swap nibbles
          movwf  PORTD          ;Write lower nibble
          bcf  PORTE,1          ;Drive E pin low so LCD will process byte
          rcall  T40            ;Wait 40 usec
          bsf  PORTE,0          ;Drive RS pin high for displayable characters
          movf  PREINC0,W       ;Increment pointer, then get next byte
        UNTIL_  .Z.             ;Is it zero?
       return

;;;;;;; T40 subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Pause for 40 microseconds  or 40/0.4 = 100 clock cycles.
; Assumes 10/4 = 2.5 MHz internal clock rate.

T40
        movlw  100/3            ;Each REPEAT loop takes 3 cycles
        movwf  COUNT
        REPEAT_
          decf  COUNT,F
        UNTIL_  .Z.
        return
        
LCDstr  db  0x33,0x32,0x28,0x01,0x0c,0x06,0x00  ;Initialization string for LCD
        
;;;;;;; LoadBuffer subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine loads data into a buffer formatted correctly for the LCD display.
; It assumes the position of the data is loaded via lfsr 0

LoadBuffer
;        movf DISPLAYLEN,W
		
		
        clrf POSTDEC0              ;first set the 0x00 terminator
        
        IF_ DISPLAYNUMH,7 == 1    ;check for a negative number
          negf DISPLAYNUML        ;if it is, form the 2s compliment
   		  comf DISPLAYNUMH, F
          IF_ .C.
		    incf DISPLAYNUMH, F
          ENDIF_
        ENDIF_
        REPEAT_
          movlw 10                ;We need to divide 10 from the the value to produce
                                  ;the division
          movwf BARGB0            ;Store 10 in the B argument of divide (A/B)
          
          movff DISPLAYNUMH, AARGB0  ;Store the high byte in the value of A for the divide
          movff DISPLAYNUML, AARGB1  ;Store the low byte in the value of A for the divide
          call FXD1608U           ;Perform the division function
          movlw 0x30              ;Load an ASCII adjuster
          addwf REMB0, W          ;To add to the remainder of the division
          movwf POSTDEC0          ;Move the value into the buffer and decrement the position
          movff AARGB0, DISPLAYNUMH ;Set the remaining value for further division
          movff AARGB1, DISPLAYNUML ;Set the remaining value for further division
          decf DISPLAYLEN,F       ;decrement the remaining length
          IF_ .NZ.
            movf AARGB0,W
            iorwf AARGB1,W
          ENDIF_
        UNTIL_ .Z.                ;until it becomes zero
        
        movf DISPLAYLEN,F       ;check the display length to see if we need spaces
        WHILE_ .NZ.
          movlw 0x20
          movwf POSTDEC0
          decf DISPLAYLEN,F       ;decrement the remaining length
        ENDWHILE_
        return


;;;;;;; PotDisplay subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine briefly displays a range between 0 and 8 of the Potentiometer.

PotDisplay
        decf  HUNDRED,F         ;Decrement hundred loop counter and return if not zero
        IF_ .Z.
          MOVLF  100,HUNDRED    ;Reinitialize BLNKCNT
          call ReadPot          ;sends the potentiometers value to the ADC
          movf POTVALUE,W       ;copies potvalue to the wreg
          mullw 9               ;multiplies potvalue(in wreg) by 9
          movff PRODH, SMALLPOT  ;get the upper byte and store it in smallpot
          
          movf SMALLPOT, W      ; multiply the potentiometer value by 12 to generate the 
          mullw 12              ; motor stepping rate between 0 and 96 (in steps/second)
          movff PRODL, STEPRATE  ;save the stepping rate in variable STEPRATE
                    
          call RateDisplay      ;Show the motor rate on the computer
        ENDIF_
        return
        
;;;;;;; ReadPot subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine reads the potentiometer and puts the upper byte into ADRESH

ReadPot
        bcf ADCON0,3            ;select pot input to ADC
        bcf ADCON0,4            ;
        bcf ADCON1,ADFM         ;Left Justify data
        movlw 15                ;initalize wait loop
        REPEAT_                 ;wait loop for 15 iterations
          decf WREG, F
        UNTIL_ .Z.
        bsf ADCON0,GO_DONE        ;initiate Conversation
        REPEAT_
        UNTIL_  ADCON0,GO_DONE == 0    ;wait for completion of conversion
        movff ADRESH, POTVALUE    ;sends the ADC value to POTVALUE
        return                    ;return with result in ADRESH
        
;;;;;;; RateDisplay subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine sends the value of the variable STEPRATE to the computer as ASCII digits

RateDisplay
        movlw 0
        movwf DISPLAYNUMH
        movff STEPRATE,DISPLAYNUML
        movlw 2
        movwf DISPLAYLEN
        lfsr 0,DISP_RATE+3
        call LoadBuffer
        movlw 0xc0
        movwf DISP_RATE
        lfsr 0,DISP_RATE
        call DisplayV
        
        
        ;movlw 10                ;We need to divide 10 from the STEPRATE to produce
                                ;an ASCII value to transmit
        ;movwf BARGB0            ;Store 10 in the B argument of divide (A/B)
        ;movff STEPRATE, AARGB0  ;Store the step rate in the value of A for the divide
        ;call FXD0808U           ;Perform the division function
        ;movlw 0x0d              ;This sends the loads the carriage return value
        ;call TXbyte             ;And send it to the computer
        ;movlw 0x30              ;Load an ASCII adjuster
        ;addwf AARGB0, W         ;To add to the integer result of the division
        ;call TXbyte             ;Send this ASCII value to the computer
        ;movlw 0x30              ;Load an ASCII adjuster
        ;addwf REMB0, W          ;To add to the remainder of the division
        ;call TXbyte             ;Send this ASCII value to the computer
        return
        
;;;;;;; TXbyte subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine first waits on the UART if a byte is in the process of being
; sent.  Then it sends the content of WREG to the PC.

TXbyte
        REPEAT_                 ;If an earlier transmission is still
        UNTIL_ PIR1,TXIF == 1   ;in progress then wait
        movwf TXREG             ;send new byte from wreg
        return	

;;;;;;; ControlStepping subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine sets the stepper motor speed in accordance to the STEPRATE variable
; Also, this subroutine steps the motor the number of steps specified by the NUMSTPS(H:L) variable

ControlStepping
        movf NUMSTPSL,w         ;xor numstpsl with numstpsh to see if it is an overall zero value number
        iorwf NUMSTPSH, w
        IF_ .NZ.                ;check for non-zero value in xor of NUMSTP(H:L)
          movf STEPRATE, W          ;Add STEPRATE into the step motor accumulator
          addwf ACCUM, F
          IF_ .C.                   ;If there is an overflow from the accumulator then...
            IF_ NUMSTPSH,7 == 1  ;check for ccw
              decf POSL,f      ;decrement the position
              IF_ .B.
                decf POSH, f
              ENDIF_
              
              bcf  PORTB,RB5            ;set direction to CCW
              incf NUMSTPSL,f   ;increment the number of steps left to be taken
              IF_ .C.
                incf NUMSTPSH, f		
              ENDIF_
            ELSE_               ;check for cw
            incf POSL,f   ;increment the current stepper motor position
              IF_ .C.
                incf POSH, f		
              ENDIF_
              
              bsf  PORTB,RB5            ;set step direction to CW
              decf NUMSTPSL,f   ;decrement the number of steps left to be taken
              IF_ .B.
                decf NUMSTPSH, f
              ENDIF_
            ENDIF_
            btg PORTB,RB1           ;Toggle RB1 for motor time measurement
            bsf  PORTB,RB4          ;Set the stepper motor movement
            bcf  PORTB,RB4        ;Clear the stepper motor movement
            movlw 100               ;Move 100 into WREG for subtraction
            subwf ACCUM,F             ;Subtract 100 to drop the counter down from the overflow
            movff POSH,DISPLAYNUMH   ;Move the position codes into the buffer variables
            movff POSL,DISPLAYNUML
            movlw 4                   ;Set the display length
            movwf DISPLAYLEN
            lfsr 0,DISP_POS+6          ;Move DISP_POS into the register for buffer formatting
            call LoadBuffer             ;Format the buffer
            movlw 0x81                 ;Load the LCD control code
            movwf DISP_POS
            movlw 0x20                 ;Set a space at the beginning for as a formatting break
            movwf DISP_POS+1
            IF_ POSH,7 == 1            ;Check for a negative number in the position variable (POS)
              movlw 0x2d               ;If it's negative, load a - sign
            ELSE_
              movf POSH,W              ;Otherwise, we need to check if its either 0 or over 0
              iorwf POSL,W
              IF_ .NZ.
                movlw 0x2b           ;If positive, use a plus sign
              ELSE_
                movlw 0x20           ;otherwise, load a space
              ENDIF_
            ENDIF_
            movwf DISPLAYSIGN         ;Set the sign into the beginning of the string
            lfsr 0,DISP_POS+5
            movlw 0x20                ;Check for the first space
            xorwf POSTDEC0,w
            REPEAT_
              movlw 0x20              ;Loop through the entire code
              xorwf POSTDEC0,w
            UNTIL_ .Z.
            iorwf PREINC0, w
            movff DISPLAYSIGN, POSTINC0  ;Once the first space is found, add the sign in
            lfsr 0,DISP_POS
            call DisplayV
          ENDIF_
        ENDIF_
        
        return
        
;;;;;;; BlinkAlive subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine briefly blinks the LED next to the PIC every two seconds.

BlinkAlive
        bsf  PORTA,RA4          ;Turn off LED
        decf  ALIVECNT,F        ;Decrement loop counter and return if not zero
        IF_ .Z.
          MOVLF  200,ALIVECNT   ;Reinitialize BLNKCNT
          bcf  PORTA,RA4        ;Turn on LED for ten milliseconds every 2 sec
        ENDIF_
        return

        
;;;;;;; ReadKeypad subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;Reads the input from the keypad

ReadKeypad
        bsf ADCON0,3            ;select keypad input to ADC
        bsf ADCON0,4            ;
        bsf ADCON1,ADFM         ;Right Justify Data
        movlw 15                ;initalize wait loop
        REPEAT_                 ;wait loop for 35 iterations
          decf WREG, F
        UNTIL_ .Z.
        bsf ADCON0,GO_DONE      	   ;initiate Conversation
        REPEAT_
        UNTIL_  ADCON0,GO_DONE == 0    ;wait for completion of conversion
        return                 		   ;return with result in ADRESH

;;;;;;; SendStrg subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine sends keypress values to the computer.

SendStrg
        movlw 0x0d              ;This sends the carriage return value
        call TXbyte             ;And send it to the computer
        movlw 0x0a              ;This sends the linefeed value
        call TXbyte             ;And send it to the computer
        movf SIGN,w             ;load sign into wreg			
        call TXbyte             ;And send it to the computer
        lfsr 0, KEYSTRG         ;puts address of keystring into memory
        movf KEYSTRG,w          ;loads digit of keystring into wreg
        REPEAT_
          call TXbyte           ;And send it to the computer
          movf PREINC0,W  
        UNTIL_ .Z.
        movlw 0x0d              ;This sends  the carriage return value
        call TXbyte             ;And send it to the computer
        movlw 0x0a              ;This sends  the linefeed value
        call TXbyte             ;And send it to the computer
        return

        
;;;;;;; ResetKeypad subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine clears keypress information.

ResetKeypad
		
        clrf NUMH               ;clear numh variable
        clrf NUML               ;clear numl variable
        movlw 0x20
        movwf KEYSTRG            ;clear keystrng locations in ram
        movwf KEYSTRG+1
        movwf KEYSTRG+2
        movlw 0x30
        movwf KEYSTRG+3        
;        call ByteOutBuffer

        ;lfsr 0,KEYSTRG          ;remap the location of keystrng
        return
        
        
;;;;;;; Keypad subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine checks for a new keypress.
; This is done by implementing a statemachine which acts both as a button debouncer
; as well as a check to see if the key has been released (thus a new key is being entered).
;

Keypad
        call ReadKeypad
        IF_ ADRESH,0 == 1       ;if bit 0 of adresh is 1 then clear bits 0 and 1 of flags
          bcf FLAGS,0
          bcf FLAGS,1
        ELSE_
          IF_ FLAGS,0 == 0
            IF_ FLAGS,1 == 0      ;if FLAGS = 00
              bsf FLAGS,0		     ;set flags bit 0 to 1         
            ENDIF_
          ELSE_    		
            IF_ FLAGS,1 == 0      ;if FLAGS = 01
              bsf FLAGS,1		     ;set flags bit 1 to 1
              movff ADRESL, TBLPTRL  ;copy adresl to tblptrl
              movlw 0x5f        ;put 0x5f into tblptrh
              movwf TBLPTRH
              tblrd*            ;copies keyvalue of pressed button into tablat
              
              movlw 0x2a        ;xor the tablat with 2a resulting in a 0 if *(0x2a) is pressed
              xorwf TABLAT,w
              IF_ .Z.           ;if * is pressed
                movlw 0x2b      ;store + in sign
                movwf SIGN
                movf NUML, w    ;adds the NUMH:NUML values to the NUMSTPSH:NUMSTPSL values storing in NUMH:NUML
                addwf NUMSTPSL, f	
                movf NUMH, w
                addwfc NUMSTPSH,f
;                call SendStrg   ;sends values to computer
                call ResetKeypad  ;clears numh:numl
              ELSE_
                movlw 0x23      ;23				;xor the tablat with 23 resulting in a 0 if #(0x23) is pressed
                xorwf TABLAT,w
                IF_ .Z.         ;if # is pressed
                  movlw 0x2d    ;store - in sign
                  movwf SIGN
                  movf NUML, w  ;adds the NUMH:NUML values to the NUMSTPSH:NUMSTPSL values storing in NUMH:NUML
                  subwf NUMSTPSL, f
                  movf NUMH, w
                  subwfb NUMSTPSH,f
                  
;                  call SendStrg  ;sends values to computer
                  call ResetKeypad  ;clears numh:numl           	        
                ELSE_
                  movf NUMH, w
                  IF_ .NZ.
                    call ResetKeypad  ;resets the keypad

                  ENDIF_
                  ;movff TABLAT, POSTINC0  ;store the tablat value in keystrng
                  movff KEYSTRG+1, KEYSTRG
                  movff KEYSTRG+2, KEYSTRG+1
                  movff KEYSTRG+3, KEYSTRG+2
                  movff TABLAT, KEYSTRG+3
                  ;call ByteOutBuffer
                  movf NUML, W  ;multiply NUML by 10 storing the result in NUMH:NUML
                  mullw 10
                  movff PRODH, NUMH
                  movff PRODL, NUML
                  movlw 0x30    ;subtract 0x30 from tablat value
                  subwf TABLAT, w
                  addwf NUML, f  ;add tablat value to NUMH:NUML
                  movlw 0
                  addwfc NUMH,f
                ENDIF_
              call ByteOutBuffer
              ENDIF_
            ENDIF_
          ENDIF_
        ENDIF_
        return

;;;;;;; ByteOutBuffer subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This subroutine updates the display for each new keypress.
;

ByteOutBuffer
		movlw 0xc4			;sets the Cursor position code into the buffer
		movwf DISP_BUF
		movlw 0x20			;this portion of the code removes leading space characters replacing them with zeros
		xorwf KEYSTRG,w
		IF_ .Z.
		  movlw 0x30
		  movwf KEYSTRG
		  movlw 0x20
		  xorwf KEYSTRG+1,w
		  IF_ .Z.
		    movlw 0x30
		    movwf KEYSTRG+1
		    movlw 0x20
		    xorwf KEYSTRG+2,w
		    IF_ .Z.
		      movlw 0x30
		      movwf KEYSTRG+2
		    ENDIF_
		  ENDIF_  
		ENDIF_
		
		movlw 0x30			;this portion of the code removes leading zero characters replacing them with spaces
		xorwf KEYSTRG,w
		IF_ .Z.
		  movlw 0x20
		  movwf KEYSTRG
		  movlw 0x30
		  xorwf KEYSTRG+1,w
		  IF_ .Z.
		    movlw 0x20
		    movwf KEYSTRG+1
		    movlw 0x30
		    xorwf KEYSTRG+2,w
		    IF_ .Z.
		      movlw 0x20
		      movwf KEYSTRG+2
		    ENDIF_
		  ENDIF_  
		ENDIF_
		
		movff KEYSTRG, DISP_BUF+1			;this portion of the code copies the characters stored in the key buffer into the display buffer
		movff KEYSTRG+1, DISP_BUF+2
		movff KEYSTRG+2, DISP_BUF+3
		movff KEYSTRG+3, DISP_BUF+4
		movlw 0x00							;terminating the display buffer with a null character
		movwf DISP_BUF+5
        return
        
        
;;;;;;; LoopTime subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; When Timer2 rolls over and sets the TMR2IF flag, approximately ten
; milliseconds have passed since the last time the flag was set.  The flag
; is cleared and execution returns from the subroutine to the mainline loop.

LoopTime
        REPEAT_
        UNTIL_  PIR1,TMR2IF == 1  ;Wait for completion of ten milliseconds
        bcf  PIR1,TMR2IF        ;Clear flag
        return  

        #include <C:\MATH18\FXD0808U.inc>
        #include <C:\MATH18\FXD1608U.inc>
        #include <KeyCodes.inc>
        end

