please dont rip this site

December 2003 MassMind newsletter

The main newsletter is here

; gps_lcd.src
;
; DBeals Apr 19, 2004
; 
; Receive GPS serial chars, format, send display chars to LCD.
; 
;-------------------------------------------------------
; Scenix SX18 processor
; Seetron 2 row by 20 character LCD 
; Garmin 25LVC GPS sensor
; (see notes at the end for more device information)
;-------------------------------------------------------
; Port A   bit 0  pin ??         Output RS232 serial (TTL level) to LCD
;          bit 1                 unused
;          bit 2                 unused
;          bit 3                 unused
;
; Port B   bit 0  pin ??         input RS232 serial (TTL level) from GPS
;          bit 1                 unused
;          bit 2                 unused
;          bit 3                 unused
;          bit 4                 unused
;          bit 5                 unused
;          bit 6                 unused
;          bit 7                 unused
;-------------------------------------------------------
; data ram:
;   00-07                        8 CPU registers
;   08-0F                        8 bytes of global RAM
;   10-1F, 30-3F, 50-5F...F0-FF  8 banks of 16 bytes user RAM.
;
; code rom: 
;   4 banks of 512 ($200) bytes.
;   org $000 = bank 0, $200 = bank 1, $400 = bank2, $600 = bank3
;
;-------------------------------------------------------

device SX18, OSCHS, TURBO, STACKX, OPTIONX

IRC_CAL IRC_FAST

freq 20_000_000
reset start_point

;-------------------------------------------------------
; global variables

org             7               ;      (the SX18 can use RAM 7 as global)
RS232out        ds      1       ;  7   LCD RS232 out character
RS232in         ds      1       ;  8   GPS RS232 in character
timer1          ds      1       ;  9   RS232 loop timer
msgBytes        ds      1       ;  A   counts bytes within GPS message field
bitCount        ds      1       ;  B   LCD display loop
temp            ds      1       ;  C   DDMM decoding (and other places)
pointer1        ds      1       ;  D   generic RAM pointer
pointer2        ds      1       ;  E   me too
i               ds      1       ;  F   generic loop counter

; time and latitude characters

org                     $10
bank_ten        =       $
latASCII        =       $10     ; lat:  10 ASCII "03749.2345" 0DDMM.MMMM format 
latEnd          =       $19   
timeStart       =       $1A     ; time: 6 characters HHMISS in 24:60:60 format
timeEnd         =       $1F     ; 
                              
; longitude, status and altitude chars

org                     $30
bank_lonstat    =       $
lonASCII        =       $30     ; lon: 10 chars "12232.3580"  DDDMM.MMMM format
lonEnd          =       $39
altStart        =       $3A     ; alt: from 0 to 5 chars "122.6" meters
altEnd          =       $3F     ; FIXME - should this be $3E?
statusChar      =       $3F     ; GPS status: one char

; ASCII-binary conversions and multiplier locations

org                     $50     ; 
bank_math       =       $
multStart       =       $       ; this needs to precede the four "mult"s
mult0           ds      1       ; 4 byte input to 1 x 4 multiply
mult1           ds      1       ; (also used by subtract routine)
mult2           ds      1
mult3           ds      1
accumStart      =       $       ; this needs to precede the 4 accums
accum0          ds      1       ; 4 byte output from several math routines
accum1          ds      1
accum2          ds      1
accum3          ds      1
eight           ds      1       ; 1 byte input to 1 x 4 multiply routine
counter         ds      1       ; "counter" used in the multiply loop
negFlag         ds      1       ; set to 1 if subtraction is negative
math0           ds      1       ; spares
math1           ds      1       ;
math2           ds      1       ;
math3           ds      1       ;
                                ; 1 unused byte

; 4 binary 4-byte locations

lonToGoTo       =       $70     ;  4 bytes: lon to go to, binary
lonAtStart      =       $74     ;  4 bytes: lon started from, binary
latToGoTo       =       $78     ;  4 bytes: lat to go to, binary
latAtStart      =       $7C     ;  4 bytes: lat started from, binary

; speed and direction

dirStart        =       $90     ; dir: 90 - 95 000.0
dirEnd          =       $95     ; FIXME is 95 or 96 good enuf?
speedStart      =       $96     ; speed: 96 - 9B 0000.0
speedEnd        =       $9B     ; 
                                ; 4? unused bytes in bank $90
 
; decimal display

org                     $B0     ; binary to decimal conversion area
bank_toASCII    =       $
counter2        ds      1
hiBin           ds      1
loBin           ds      1
hiCmp           ds      1
loCmp           ds      1
fiveDigits      =       $       ; here begins 5 decimal digits
digit5          ds      1
digit4          ds      1
digit3          ds      1
digit2          ds      1
digit1          ds      1
                                ; 6 unused bytes

org                     $D0     ; unused space for 16 vars

; last bank has assorted stuff - timer vars, GPS msg counters, etc.

org                     $F0
bank_assorted   =       $       ; F0
timer2          ds      1       ; F1
timer3          ds      1       ; F2
timer4          ds      1       ; F3
string          ds      1       ; F4
vlongLow        ds      1       ; F5
nybs            ds      1       ; F6
hexByte         ds      1       ; F7
msgLen          ds      1       ; F8    count GPS chars per specific message
msgType         ds      1       ; F9    Position vs. Velocity GPS NMEA msg
commaCount      ds      1       ; FA    detect fields within the NMEA msg
screenToShow    ds      1       ; FB    toggle thru several screens as this incrs
savedStart      ds      1       ; FC    flag for initial pos save
charsPerLine    ds      1       ; FD
                                ; FE, FF unused space

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

org           0

;-------------------------------------------------------
; interrupt service routine (currently unused)
;-------------------------------------------------------

isr

        reti

;-------------------------------------------------------
; main program
;-------------------------------------------------------

start_point  

        mode    $0F             ; prepare for configuring direction mode 
                                ; see pg. 144 SZ-key dev man
        mov     !ra,#%11111110  ; only a0 outie; the rest innie
        mov     !rb,#%11111111  ; all bs innies
        mode    $0E             ; prepare for configuring pull-up resistor mode
        mov     !ra, #%0000     ; enable all port A pull-ups
;
;                               ; these not currently used; here for reference:
;       mode    $0D             ; CMOS/TTL input (n/a when Schmidt used)
;       mov     !rb,#%00000000  ; set all to CMOS
;       mode    $0C             ; schmidt trigger
;       mov     !rb,#%00000000  ; all schmidts enabled (pg. 59 and 140)
;       mode    $0B             ; wake-up enable
;       mov     !rb,#%11111110  ; wake-up interrupt edge bit 4 (pin 1)
;       mode    $0A             ; rising/falling: default falling edge
;       mode    $09             ; swap W, edge det status (pg. 61)
;       mov     !rb, %00000000  ; clear edge status 
                                ; (and store state in "w" reg)
;-------------------------------------------------------
; main loop
;-------------------------------------------------------

main
        call    @init
        call    @hello

;       call    subtest
;       call    multTest
;       call    @binToASCIITest

        jmp     @getGPSMsgGrp        ; get GPS message; display to LCD
 
        jmp     main

; End of main 

;-------------------------------------------------------
; some strings 

shello  dw      'Hello World! HaWaYa?',0
sline2  dw      'GPS LCD ver 04.19.04',0
nosync  dw      'No signal',0
latToGo dw      '03741.3430',0
lonToGo dw      '12153.3754',0

;-------------------------------------------------------
; Can't call a function located in the second half of any code page so
; compensate by using this jump table which can reference functions on any
; part of any page. 
; This table must exist entirely in a first page half.
; If this grows too large, continue on the top half of other code pages.
; Check gps_lcd.lst: last table entry must be below $FF. "$B2" as of last check.

init            jmp @_init
hello           jmp @_hello
getByte         jmp @_getByte
sendByte        jmp @_sendByte
sendSpace       jmp @_sendSpace
sendString      jmp @_sendString
clearScreen     jmp @_clearScreen
lcdLine1        jmp @_lcdLine1
lcdLine2        jmp @_lcdLine2
sendHexByte     jmp @_sendHexByte
hexNybble       jmp @_hexNybble
halfDelay       jmp @_halfDelay
delay9600       jmp @_delay9600
delay1ms        jmp @_delay1ms
delay100ms      jmp @_delay100ms
delay1sec       jmp @_delay1sec
longLow         jmp @_longLow
multiply32      jmp @_multiply32
subtract32      jmp @_subtract32
increment32     jmp @_increment32
copy32          jmp @_copy32
showHex32       jmp @_showHex32
shift32         jmp @_shift32
clearAccum      jmp @_clearAccum
multPreset      jmp @_multPreset
DDMMToBin       jmp @_DDMMToBin
Lat2Feet        jmp @_Lat2Feet
Lon2Feet        jmp @_Lon2Feet
loadLatLon      jmp @_loadLatLon
multTest        jmp @_multTest
latToBinTest    jmp @_latToBinTest
latSubTest      jmp @_latSubTest
binToASCII      jmp @_binToASCII
binToASCIITest  jmp @_binToASCIITest
showDirSpeed    jmp @_showDirSpeed
showDistHome    jmp @_showDistHome
showLatLon      jmp @_showLatLon
showTimeAlt     jmp @_showTimeAlt
padLine         jmp @_padLine
loader          jmp @_loader

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

_init

; to init the LCD, we need to set its bit low on power-up and
; keep it there for a second, otherwise the LCD driver goes into debug mode.

        mov     ra, #0            ; initialize the LCD bit
        call    @delay1sec

        bank    bank_assorted     ; set flag: not yet saved the
        mov     savedStart, #0    ; initial position lat and lon

        mov     FSR, #latASCII    ; preset the lat hundreds digit to 0 so
        mov     ISR, #'0'         ; Lat "DD" can be processed like lon "DDD"

        retp

;--------------------------------------------------------------------
; GPS-receive,parse,LCD-send section.
; These aren't calleable subroutines - they jump from code block to
; code block as individual RS232 characters arrive and are processed, 
; then jump back to one of two re-entry points:
; 1. the top, just below here, to get a new message group, or
; 2. just below that, to get a new character of the current group.
;--------------------------------------------------------------------

getGPSMsgGrp

; Begin wait for a group of GPS NMEA messages.
;
; Once a second, the GPS sensor sends a series of the NMEA messages,
; then pauses for about 3/4 of a second.
; When this program is synchronized to the sensor, the code will
; wait here for the next message group to start to arrive. 
; It will recieve all message group characters, process the received 
; data, display information onto the LCD, then jump back here to wait 
; for the next message group. 
; On any errors, the code jumps back here to begin a fresh message group wait.


        bank    bank_assorted
        clr     msgLen                  ; length of single NMEA message.
        clr     commaCount              ; count fields within NMEA message group.

        mov     FSR, #altStart          ; preset the altitude.
:loop1  mov     IND, #' '               ; Altitude and speed are variable length.
        inc     FSR                     ; when out of sync nothing arrives.
        cjb     FSR, #altEnd, @:loop1   ; But when in sync, only the necessary number
                                        ; of chars arrive to describe the current value.
        mov     FSR, #speedStart        ; So if we go from 100 to 99 meters altitude,
:loop2  mov     IND, #'_'               ; the "1" stays unless it is explicitly
        inc     FSR                     ; written over.
        cjb     FSR, #speedEnd, @:loop2

        mov     FSR, #dirStart          ; preset direction. May be unnecessary.
:loop3  mov     IND, #'_'
        inc     FSR
        cjb     FSR, #dirEnd, @:loop3

        call    @longLow                ; wait for long pause in received RS232


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

getGPSchar

        call    @getByte                        ; get the next RS232 GPS character
                                                ; 
        bank    bank_assorted                   ; An NMEA GPS msg begins with "$"
        inc     msgLen                          ; Count received chars in this msg.
        cjne    RS232in, #'$', @parseMsg        ; If not "$" then parser gets it.
        mov     msgLen, #1                      ; If is "$" then reset counter and
        jmp     @getGPSchar                     ; wait for next char.


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

parseMsg

; Use characters 2-6 to determine the message type.
;   GPGGA: time, lat, lon, alt
;   PGRMV: velocity N, E, Up
;   PGRMT: status message always sent once per minute. not real interesting.
;   GPRMC: absolute velocity and compass bearing
;   GPVTG: maybe use this someday?
;
; Once we're past character 6, process the message body
; (except if over 80 then probably an error occurred, so reset.)

        cje     msgLen, #2,  @msgChar2          ; expect 'G' or 'P'
        cjbe    msgLen, #5,  @getGPSchar        ; go get another char
        cje     msgLen, #6,  @msgChar6          ; expect 'A' or 'G'
        cjae    msgLen, #80, @getGPSMsgGrp      ; reset if count too big
        jmp     @processChar                    ; on any other message position,
                                                ; let subroutine decide what to do.

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

msgChar2

; Verify that character 2 is either P or G. 
; If G, set the message type to "P" for Position message.
; (will overwrite this if char 6 is also "G")

        cje     RS232in, #'P', @getGPSchar      ; may be info msg
        cjne    RS232in, #'G', @getGPSMsgGrp    ; by here, if not G, not ok. 
        mov     msgType, #'P'                   ; P for Position message
        jmp     @getGPSchar                     ; and wait for the next character

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

msgChar6

; Verify that character 6 is either A (GPGGA) or G (GPVTG)

        cje     RS232in, #'A', @getGPSchar      ; OK; we have GPGGA
        cjne    RS232in, #'G', @getGPSMsgGrp    ; by here, if not G, not ok. 
        mov     msgType, #'V'                   ; V for GPVTG velocity type message
        jmp     @getGPSchar                     ; go wait for the next character

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

processChar

; if this character is data, then go process it.
; if it is a field separator then go process that.
; (ahh, the velocity message terminates the last field with "*",
;  so it is a field separator in that case, also.)

        cje     RS232in, #',', @field           ; comma - go directly to "field";
        cjne    RS232in, #'*', @processData     ; if not * then go process char.
        cjne    msgType, #'V', @getGPSchar      ; if * but not vel then get next gps,
                                                ; else fall into "field".

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

field

; we have a field separator.
; Do two things:
; 1. prepare for a new field by incrementing commaCount and clearing msgBytes.
; 2. If we have received all the fields we're expecting (currently hex 16),
;    (see half a page down for more description of the commaCounts), then 
;    skip the rest of the message group and display what we've got.
;    Otherwise go get yet another GPS character.

        inc     commaCount 
        clr     msgBytes
        cje     commaCount, #$16,  @doDisplay
        jmp     @getGPSchar

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

processData

; we have a data character.
; decide what to do based on the current commaCount.
; Note: let the commaCount manage the entire message group, based
; on the GPS sensor always sending the messages in the same order.
; Is this assumption warranted? It works so far...
; Otherwise would need to test both message type and commaCount...
;
;        1      2         3 4          5 6 7  8   9    A B     C DE
;  $GPGGA,205343,3749.9327,N,12232.3580,W,1,08,1.1,12.5,M,-28.0,M,,*42
;  $PGRMV,0.0,0.0,0.0*5C
;  $PGRMT,GPS 25-LVC VER 2.50 ,P,P,R,R,P,,19,R*15
;
; as of 3/28/04 no more PGRMV message.
; instead, configured GPS to send GPVTG (Track Made Good and Ground Speed)
;
; commas 1 2 3 4 5 6 7 8
; or, if continuing the commaCount from the previous field...
;        F 0 1 2 3 4 5 6       <- that is, 0x16 is the last expected comma.
; fields  1   2   3   4  5
;  $GPVTG, ,T, ,M, ,N, ,K *..
;
; 1: true course: 000.0 to 359.0 degrees
; 2: magnetic course 000.0 to 359.0
; 3: speed: 000.0 to 999.9 knots
; 4. speed: 0000.0 to 1851.8 kilometers per hour
; 5: mode - do I care? not sure, yet.

; when saving individual characters, detect field position by the count
; of the comma preceding the field.

        cje     commaCount, #$01, @timeField      ; 6 time chars
        cje     commaCount, #$02, @latField       ; 8 latitude chars 
        cje     commaCount, #$04, @lonField       ; 9 longitude chars
        cje     commaCount, #$06, @statusField    ; 1 status char;
        cje     commaCount, #$09, @altField       ; 0 to 5 altitude chars
        cje     commaCount, #$0F, @dirField       ; 0 to 5 direction chars
        cje     commaCount, #$15, @speedField     ; 0 to 6 speed chars

; if no more fields are of interest then just go get another GPS character:

        jmp    @getGPSchar

;-------------------------------------------
; Save characters into individual RAM banks.

timeField                               ; always 6 characters: $10 to $15
        mov     FSR, #timeStart
        jmp     @saveChar
latField                                ; probably always 8 characters: $16 to $1D 
        mov     FSR, #latASCII          ; GPS sends only 2 digits of lat degrees
        inc     FSR                     ; so incr FSR once before using it
        jmp     @saveChar              
lonField                                ; probably always 9 chars $30 to $38
        mov     FSR, #lonASCII
        jmp     @saveChar
statusField                             ; always 1
        mov     FSR, #statusChar
        jmp     @saveChar
altField                                ; varies from 0 to 4 or 5: $39 to $3D?
        mov     FSR, #altStart
        jmp     @saveChar
dirField                                ; varies from 0 to 6
        mov     FSR, #dirStart
        jmp     @saveChar
speedField                              ; varies from 0 to 6
        mov     FSR, #speedStart
        jmp     @saveChar

; add additional field storage here, if you want any

saveChar                                

; saveChar: jump here with FSR containing the start address of 
; the field that we're saving to. So if we add the current value
; of the msgBytes to the FSR, then we have the storage of this character
; within each field. Yeah, if more chars arrive than we're expecting
; then we'll overwrite some other value...Lets just see if that ever happens.
; (Oh no, a hacker could feed a virus in and hijack the project via field overflow!)

        add     FSR, msgBytes
        mov     IND, RS232in
        inc     msgBytes
        jmp     @getGPSchar     ; go get the next char  

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

doDisplay

; Process and display information.

; The first time that a good set of satellite data is seen, 
; save the latitude and longitude binary numbers.


        bank    bank_assorted
        cje     savedStart, #1, @:continue   ; if 1 then already saved.
        bank    bank_lonstat
        cjne    statusChar, #'1', @:continue ; unless 1, sat data not good

  
        mov     pointer1, #latASCII          ; point to Latitude 0DDMM.MMMM
        call    @DDMMToBin                   ; convert to binary
        mov     pointer1, #accumStart        ; and save
        mov     pointer2, #latAtStart    
        call    @copy32                   

        mov     pointer1, #lonASCII          ; point to Longitude DDDMM.MMMM
        call    @DDMMToBin                   ; convert to binary
        mov     pointer1, #accumStart        ; and save
        mov     pointer2, #lonAtStart
        call    @copy32              

        bank    bank_assorted                ; OK, we've saved the
        mov     savedStart, #1               ; starting location!

:continue

; Toggle thru several screens; approximately 1 per second.
;
; 0: display current position and time 
; 1: display elapsed time since first satellite sync
; 2: display distance from position of first satellite sync
; 3: (todo) display distance to programmed waypoint
; 4: display current velocity, compass heading

        bank    bank_assorted

        inc     screenToShow
        cjb     screenToShow, #4, @showScreen   ; "4" is total number of screens 
        clr     screenToShow

showScreen:

        cje     screenToShow, #0, @showLatLonspeed

        cje     screenToShow, #1, @showLatLonDH
;       cje     screenToShow, #2, @showLatLonDH
;       cje     screenToShow, #3, @showLatLonDH
        cje     screenToShow, #2, @showLatLonspeed
        cje     screenToShow, #3, @showLatLonAlt
;       cje     screenToShow, #3, @showBinary


; Any individual screen display routine may optionally delay for some
; length of time, like a second or so, to better show its contents,
; before jumping back to wait for the next message group.
; This may cause the program to skip message groups. Ordinarily
; this should be no big deal - missing a group is always a possibility,
; and every GPS data processing routine must allow for this.
; But ideally we will be able to catch every group.
;
; Each of the screen display routines will conclude by jumping
; to wait for a new GPS message group as in the following jmp,
; shown just below, although this particular jmp should never
; be reached if the above screenToShow is working properly:

        jmp     @getGPSMsgGrp

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

showLatLonAlt

; On the top line show ASCII Latitude, Longitude.
; On the second line show the time and altitude (if in sync)

        call    @lcdLine1
        call    @showLatLon
        call    @lcdLine2
        call    @showTimeAlt
        jmp     @getGPSMsgGrp

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

showLatLonSpeed

; On the top line show ASCII Latitude, Longitude.
; On the second line show the speed and direction 
     
        call    @lcdLine1
        call    @showLatLon
        call    @lcdLine2
        call    @showDirSpeed  
        jmp     @getGPSMsgGrp

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

showLatLonDH

; On the top line show ASCII Latitude, Longitude.
; On the second line show the distance from the start.
     
        call    @lcdLine1
        call    @showLatLon
        call    @lcdLine2
        call    @showDistHome
        jmp     @getGPSMsgGrp

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

showBinary

; convert the current longitude and latitude to 4 byte integers 
; representing 1/10000 of a minute, and display.

        call    @clearScreen           ; prepare the LCD to show results

        mov     pointer1, #latASCII    ; point (1 below) Latitude DDMM.MMMM
        call    @DDMMToBin             ; convert to binary
        call    @lcdLine1              ; On the LCD top line...
        mov     pointer1, #accumStart
        call    @showHex32             ; show 4 hex bytes

        mov     pointer1, #lonASCII    ; point to Longitude DDDMM.MMMM
        call    @DDMMToBin             ; convert to binary
        call    @lcdLine2              ; On the LCD bottom line...
        mov     pointer1, #accumStart
        call    @showHex32             ; show 4 hex bytes

        jmp     @getGPSMsgGrp


; END of GPS receive message jump routines
;
;**************************************************************************
; code below are subroutines.
; general groups:
;
; Latitude, Longitude management group
; Math group
; LCD group
; RS232 group
; test routine group
;

;**************************************************************************
; Latitude, Longitude management group
;

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

_showDistHome

; Display the distance in feet from first sync to the current LCD line.
 
        bank    bank_assorted
        cjne    savedStart, #1, @:continue

        mov     pointer1, #latASCII      ; point to Lat, cvrt to binary
        call    @DDMMToBin               ; and leave result in accumulator   
        mov     pointer1, #latAtStart    ; copy original saved lat binary
        mov     pointer2, #multStart     ; to the other 4-byte register
        call    @copy32                
        call    @subtract32              ; get the diff...
        bank    bank_math
        cje     negFlag, #0, @:P0        ; test whether we're north or
        mov     RS232out, #'N'           ; south of the starting sync
        jmp     @:P1
:P0     mov     RS232out, #'S'           
:P1     call    @sendByte                ; display compass direction
        call    @Lat2Feet                ; convert difference to feet
        mov     pointer1, #accumStart  
        call    @showHex32               ; show feet as 4 hex bytes

        call    @sendSpace
        call    @sendSpace

        mov     pointer1, #lonASCII      ; point to Lon, cvrt to binary
        call    @DDMMToBin               ; and leave result in accumulator   
        mov     pointer1, #lonAtStart    ; copy original saved lon binary
        mov     pointer2, #multStart     ; to the other 4-byte register
        call    @copy32                
        call    @subtract32              ; get the diff...
        bank    bank_math
        cje     negFlag, #0, @:P2        ; test whether we're east or
        mov     RS232out, #'W'           ; west of the starting sync
        jmp     @:P3
:P2     mov     RS232out, #'E'           
:P3     call    @sendByte                ; display compass direction
        call    @Lon2Feet                ; convert difference to feet
        mov     pointer1, #accumStart  
        call    @showHex32               ; show feet as 4 hex bytes

        call    @sendSpace

:continue

        retp

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

_showLatLon

; Display the current latitude and Longitude to the current LCD line.

        mov     FSR, #latASCII           ; send 9 latitude chars
        inc     FSR                      ; skip dummy "0" at lat beginning
:loop1  mov     RS232out, IND
        call    @sendByte
        inc     FSR
        cjbe    FSR, #latEnd, @:loop1

        call    @sendSpace
              
        mov     FSR, #lonASCII           ; send 10 longitude characters
:loop2  mov     RS232out, IND
        call    @sendByte
        inc     FSR
        cjbe    FSR, #lonEnd, @:loop2

        retp

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

_showDirSpeed

; Display the current direction and speed to the current LCD line.
; (if the status says we're not in sync then just report that and quit)

        bank    bank_lonstat 
        cjne    statusChar, #'1', @saynosync

        mov     RS232out, #'D'           ; send dir label
        call    @sendByte
        call    @sendSpace

        mov     i, #5
        mov     FSR, #dirStart           ; send 6 dir chars
:loop1  mov     RS232out, IND
        call    @sendByte
        inc     FSR
        djnz    i, @:loop1

        call    @sendSpace
        mov     RS232out, #'S'           ; send speed label
        call    @sendByte
        call    @sendSpace
        
        mov     i, #6
        mov     FSR, #speedStart         ; send ? speed characters
:loop2  mov     RS232out, IND
        call    @sendByte
        inc     FSR
        djnz    i, @:loop2

        call    @padLine                 ; clear rest of line with spaces

        retp

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

_showTimeAlt

; Show time and altitude. Time is always available, but if not
; in sync then alt is not available.

        mov     FSR, #timeStart         ; send 6 time characters
:loop   mov     RS232out, IND
        call    @sendByte
        inc     FSR
        cjbe    FSR, #timeEnd, @:loop

showalt 
        call    @sendSpace
        mov     RS232out, #'h'          ; send height label
        call    @sendByte
        call    @sendSpace

        bank    bank_lonstat 
        cjne    statusChar, #'1', @saynosync

        mov     FSR, #altStart          ; send 4 or so altitude characters
:loop   mov     RS232out, IND
        call    @sendByte
        inc     FSR
        cjb     FSR, #altEnd, @:loop

        call    @padLine

        retp

saynosync
        mov     W, #nosync
        call    @sendString
        call    @padLine

        retp

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

_Lat2Feet

; Enter with mult holding the binary of the latitude that
; we want to calculate distance from.
; Return with the accumulator containing the feet from the current 
; latitude to the pointed-to latitude.
; negFlag set to 1 if we're South of the reference; 0 if North.
; one minute of Lat ~ 6072 feet; 6072/10000 = 0.6072; ~ 39/64 
 
        call    @clearAccum
        bank    bank_math         
        mov     eight, #$27             ; multiply by 39 (0x27), leaving the
        call    @multiply32             ; product in the accumulator.
        bank    bank_math            
        mov     eight, #6               ; divide by 64 by shifting 6
        call    @shift32
        retp

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

_Lon2Feet

; Enter with mult holding to the binary of the longitude that
; we want to calculate distance from.
; Return with the accumulator containing the feet from the current 
; longitude to the pointed-to longitude.
; negFlag set to 1 if we're East of the reference; 0 if West.
; one min of Lon at 37 deg ~ 4790 feet; 4790/10000 = 0.4790; ~ 122/256

        call    @clearAccum
        bank    bank_math         
        mov     eight, #$7A             ; multiply by 122 (0x7A), leaving the
        call    @multiply32             ; product in the accumulator.
        bank    bank_math         
        mov     eight, #8               ; divide by 256 by shifting 8
        call    @shift32
        retp

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

_shift32     

; divide the 4 byte number at pointer1 by (2 to the power of (eight))

        bank    bank_math
:divideby                               
        clc                             ; On rotate, carry bit would rotate
        rr      accum3                  ; into hi bit of mult3, which we
        rr      accum2                  ; don't want, so clear it.
        rr      accum1
        rr      accum0
        djnz    eight, @:divideby
        retp

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

_clearAccum

        bank    bank_math               ; prepare for a 4-byte by 1 byte
        clr     accum0                  ; multiplication.
        clr     accum1                  ; Don't touch the 4 "mult" bytes,
        clr     accum2                  ; because subtract has populated them.
        clr     accum3  
        retp

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

_DDMMToBin

; Convert a quantity in degrees, minutes and decimal minutes from
; 10 ASCII characters in the form DDDMM.MMMM to a 4 byte binary value 
; representing ten-thousanths of a minute. (about 6 inches of distance 
; when the DDMM is latitude or longitude).
; Call with "pointer1" pointing to the start of the DDDMM.MMMM to convert.
; results will be in the 4 byte accumulator.
;
; any advantage to moving the constants below to a code area lookup table?
; no, looks like would take as much work to set up each load as to
; simply load literals as below.

        call    @clearAccum

        call    @multPreset
        mov     mult3, #$03
        mov     mult2, #$93             ; hundreds of degrees to minutes: x 6000
        mov     mult1, #$87             ; minutes to 1/10000 of minute:   x 10,000
        mov     mult0, #$00             ; 600 000 000 = $03 93 87 00
        call    @multiply32

        call    @multPreset
        mov     mult2, #$5B             ; tens of degrees to minutes:   x 600
        mov     mult1, #$8D             ; minutes to 1/10000 of minute: x 10,000
        mov     mult0, #$80             ; 60 000 000 = $5B 8D 80
        call    @multiply32

        call    @multPreset
        mov     mult2, #$09             ; degrees to minutes:           x 60
        mov     mult1, #$27             ; minutes to 1/10000 of minute: x 10,000
        mov     mult0, #$C0             ; 600 000 = $09 27 C0
        call    @multiply32

        call    @multPreset
        mov     mult2, #$01
        mov     mult1, #$86             ; tens of mins to 1/10000 of minute: x 100,000
        mov     mult0, #$A0             ; 100,000 = $01 86 A0
        call    @multiply32

        call    @multPreset
        mov     mult1, #$27             ; minutes to 1/10000 of minute: x 10,000 
        mov     mult0, #$10             ; 10,000 = $27 10
        call    @multiply32
 
        inc     pointer1                ; skip the decimal point

        call    @multPreset
        mov     mult1, #$03             ; tenths of minute to 1/10000 of minute: x 1000 
        mov     mult0, #$E8             ; 1000 = $03 E8
        call    @multiply32

        call    @multPreset
        mov     mult0, #$64             ; 100 = $64
        call    @multiply32

        call    @multPreset
        mov     mult0, #$0A             ; 10 = $0A
        call    @multiply32

        call    @multPreset
        mov     mult0, #$01             ; 1
        call    @multiply32

        retp

_multPreset

        mov     FSR, pointer1            ; operate on the digit that "pointer"
        mov     temp, IND                ; currently is pointing to
        bank    bank_math                ;
        sub     temp, #'0'               ; Adjust ASCII to number
        mov     eight, temp              ; write to the single-byte multiplier loc

        mov     mult3, #$00              ; these usually must be zero. When
        mov     mult2, #$00              ; not, let the above prg overwrite 0.
        mov     mult1, #$00              

        inc     pointer1                 
        retp

;**************************************************************************
; math subroutines
;
;   multiply32    (eight) x (mult) -> (accum)
;   increment32   (pointer1)++
;   subtract32    (abs(mult - accum)) -> accum; negFlag=1 if minus
;   clear32       (pointer1) -> 0
;   showHex32     (pointer1 -> 8 hex nybbles -> LCD
;   copy32        (pointer1) -> (pointer2)
;
;
;-------------------------------------------------------

_multiply32

; Multiply the 8 bit number in "eight" times the 32 bits in mult0-mult3,
; storing the result in the 4 accumulator bytes accum0-accum3.
; Uses "counter".
; This does not preclear the accumulator, so it can be used for repeated
; multiplications where the results all need to be accumulated together,
; so should call clearmult before its first use.

        bank    bank_math
        mov     counter, #8
multloop:
        rr      eight           ; move least bit of "eight" into carry register
        jnc     @rotate         ; if it's 0, don't do any adding
        clc
        add     accum0, mult0   ; add byte 0...
        addb    accum1, C       ; then first add the carry bit into byte1,
        add     accum1, mult1   ; then add the byte, making the byte 2 carry...
        addb    accum2, C
        add     accum2, mult2
        addb    accum3, C
        add     accum3, mult3
rotate: 
        clc                     ; When we rotate, the carry bit would rotate
        rl      mult0           ; into the low bit of mult0, which we don't
        rl      mult1           ; want, so clear it.
        rl      mult2
        rl      mult3
        djnz    counter, @multloop

        retp

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

_subtract32

; Subtract 4 bytes:  Absolute value(Mult - Accum) -> Mult.
; If the result is below zero then fix: invert and add 1, a la 2's complement,
; and set a "negative" flag.

        bank    bank_math
        mov     negFlag, #0
        sub     mult0, accum0
        jc      @:p1
        inc     accum1
:p1     sub     mult1, accum1
        jc      @:p2
        inc     accum2
:p2     sub     mult2, accum2
        jc      @:p3
        inc     accum3
:p3     sub     mult3, accum3
        jc      @:done                  ; if result >= 0 then done.
        mov     negFlag, #1             ; Otherwise, set negative flag
        not     mult0                   ; and twiddle the results to be
        not     mult1                   ; as if we subtracted mult from accum.
        not     mult2                   ; (NOT the 4 bytes, then add 1)
        not     mult3
        clc
        add     mult0, #1
        addb    mult1, C  
        addb    mult2, C
        addb    mult3, C
:done   retp

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

_increment32

; increment the pointed-to set of 4 bytes
; mainly for testing various 4-byte math routines

        mov     FSR, pointer1
        add     ISR, #1         ; incr LSB, setting the carry bit appropriately
        inc     FSR
        addb    ISR, C          ; if there's a carry bit then pass it along...
        inc     FSR
        addb    ISR, C          ; and here...
        inc     FSR
        addb    ISR, C          ; me, too.
        retp

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

_copy32

; copy values of 4 bytes starting at pointer1 to 4 bytes at pointer2
; modifies pointer1, pointer2, temp

        mov     i, #4
:loop
        mov     FSR, pointer1             ; operate on the digit that "pointer"
        mov     temp, IND                 ; currently is pointing to
        mov     FSR, pointer2             ; operate on the digit that "pointer"
        mov     IND, temp
        inc     pointer1
        inc     pointer2
        djnz    i, :loop
        retp  

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

_showHex32

; show contents of four bytes in hex pointed to by "pointer1".
 
        mov     FSR, pointer1
        inc     FSR
        inc     FSR
        inc     FSR
        mov     RS232out, ISR
        call    @sendHexByte

        mov     FSR, pointer1
        inc     FSR
        inc     FSR
        mov     RS232out, ISR
        call    @sendHexByte
 
        mov     FSR, pointer1
        inc     FSR
        mov     RS232out, ISR
        call    @sendHexByte

        mov     FSR, pointer1
        mov     RS232out, ISR
        call    @sendHexByte

        retp


;**************************************************************************
; LCD functions

_hello            
        call    @clearScreen
        call    @delay100ms
                
        call    @lcdLine1
        mov     W, #shello
        call    @sendString
        call    @delay100ms

        call    @lcdLine2
        mov     W, #sline2
        call    @sendString
        
        call    @delay1sec 
        retp

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

_sendHexByte

; write value, 0-255, in "RS232out", to LCD in two hex bytes.

        bank    bank_assorted
        mov     hexByte, RS232out       ; save the value because "RS232out"
        mov     nybs, hexByte           ; is destroyed in hexNybble
        swap    nybs
        call    @hexNybble
        bank    bank_assorted
        mov     nybs, hexByte
        call    @hexNybble 
        retp

_hexNybble      
        and     nybs, #$0F              ; cvrt 4 bits to hex and send to LCD
        clc                             ; 
        csa     nybs, #$9               ; if > 9, skip jmp and do add. 
        jmp     @:label
        clc                             ; required if clearx is set
        add     nybs, #$7
:label  add     nybs, #$30
        mov     RS232out, nybs
        call    @sendByte               ; send byte to the LCDisplay
        retp

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

_sendString 

; Send 0-terminated string of up to 20 chars to LCD
      
        bank    bank_assorted
        mov     string,w
:loop                
        mov     w,string
        mov     m,#0
        iread                     ; reads value from 11-bit code address in M, W
        test    w
        jnz     @:send
        retp
:send   mov     RS232out, w
        call    @sendByte
        bank    bank_assorted
        inc     string
        jmp     @:loop        

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

_clearScreen    
    
        mov     RS232out, #$FE
        call    @sendByte
        mov     RS232out, #$1
        call    @sendByte
        bank    bank_assorted
        mov     charsPerLine, #0
        call    @delay1ms
        retp

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

_lcdLine1        

        mov     RS232out, #$FE
        call    @sendByte
        mov     RS232out, #$80
        call    @sendByte
        bank    bank_assorted
        mov     charsPerLine, #0
        retp

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

_lcdLine2

        mov     RS232out, #$FE
        call    @sendByte
        mov     RS232out, #$C0
        call    @sendByte
        bank    bank_assorted
        mov     charsPerLine, #0
        retp

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

_sendSpace

       mov     RS232out, #' '
       call    @sendByte
       retp

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

_padLine

        bank    bank_assorted
:loop
        cjae    charsPerLine, #20, @:done
        call    @sendSpace
        jmp     @:loop
:done
        retp

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

;**************************************************************************
; delay functions

_delay1sec

        bank    bank_assorted
        mov     timer4,#9
:loop   call    @delay100ms
        bank    bank_assorted
        djnz    timer4, @:loop
        retp

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

_delay100ms      
  
        bank    bank_assorted
        mov     timer3, #99
:loop   call    @delay1ms
        djnz    timer3, @:loop
        retp

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

_delay1ms       

        bank    bank_assorted
        mov     timer2, #9
:loop   call    @delay9600
        bank    bank_assorted
        djnz    timer2, @:loop
        retp

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

;**************************************************************************
; RS232 functions

_longLow        

; Loop until a GPS message arrives.
; method: loop over the time of about 2 characters, testing
; for a hi bit twice the 9600 baud bitrate. During this looping, 
; if a high bit is ever detected, restart the loop from the beginning.

        bank    bank_assorted
        mov     vlongLow, #32           ; prepare for loop
:loop
        call    @halfDelay        
        test    rb.0        

        jnz     @_longLow               ; if ever a non-zero bit, getGPSMsgGrp.
        djnz    vlongLow, @:loop

        retp

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

_getByte        

; wait for, then return an RS232 byte in the "RS232in" variable.

        bank    bank_assorted
        test    rb.0                    ; spin until bit B0 goes high
        jz      _getByte

        call    @halfDelay              ; wait til middle of start bit

        mov     bitCount, #8            ; prepare for loop
        mov     RS232in, #$0            ; and preset result
:loop
        call    @delay9600              ; wait for middle of data bit
        clc                             ; clear carry bit
        rr      RS232in                 ; prepare result for receiving next bit
        test    rb.0                    ; have we received a one or a zero?
        jnz     @:zerobt                ; skip bitset if zero
        or      RS232in, #$80           ; we got a one, so set hi bit of result byte
:zerobt djnz    bitCount, @:loop        ; next bit
                                        ; done getting bits.
        call    @delay9600              ; wait for middle of stop bit

        retp                            ; return "RS232in"

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

_sendByte

; Send the byte in "RS232out" to the LCD
; Destroys RS232out.

        mov     bitCount, #8

        setb    ra.0            ; start bit
        call    @delay9600

:loop   snb     RS232out.0      ; 8 data bits
        jmp     @:sndhi
        setb    ra.0
        jmp     @:done
:sndhi  clrb    ra.0
:done   rr      RS232out
        call    @delay9600
        djnz    bitCount, @:loop

        clrb    ra.0            ; stop bit
        call    @delay9600

        mov     temp, FSR       ; count chars sent. Used for padding
        bank    bank_assorted   ; the LCD line with spaces to the end.
        inc     charsPerLine
        mov     FSR, temp

        retp

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

_delay9600

; these are the fundamental delays for RS232, both send and receive.
; tuned via trial and error with what works to the LCD
; 215 10% ok, 205 ok, 200 ok, 198 10% ok, so use 206

        call    @halfDelay
        call    @halfDelay
        retp

_halfDelay

        mov     timer1, #206
:loop   djnz    timer1, @:loop
        retp


; This is a version that works properly using a 50 mHz resonator
; instead of the 20 mHz crystal. (But don't like the additional
; current the SX needs: 80 mA - about double that of running at 20.)
;
;halfDelay
;       mov     timer1, #110
;:loop1 djnz    timer1, @:loop1
;:loop2 djnz    timer1, @:loop2
;:loop3 djnz    timer1, @:loop3
;       retp

;**************************************************************************
; test and experimental area
;
; what about a generic loader that uses pointer1 for where to load to,
; a zero-terminated string for what to load...? Any benefit?
; But a zero-terminated string can't load a zero.
; what about instead specifying number of bytes to load?
; 
_newLoadLatLon
        mov     pointer1, latToGo
        mov     pointer2, #latASCII
        mov     i, #10
        call    @loader
        mov     pointer1, lonToGo
        mov     pointer2, #lonASCII
        mov     i, #10
        call    @loader
        retp

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

_loader
        mov     M, #0
        mov     FSR, pointer2
:loop   mov     W, pointer1
        iread
        mov     IND, W
        inc     FSR
        inc     pointer1
        djnz    i, @:loop
        retp

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

_loadLatLon

        mov     FSR, #latASCII  
        mov     IND, #'0'
        inc     FSR
        mov     IND, #'3'
        inc     FSR
        mov     IND, #'7'
        inc     FSR
        mov     IND, #'4'
        inc     FSR
        mov     IND, #'1'
        inc     FSR
        mov     IND, #'.'
        inc     FSR
        mov     IND, #'3'
        inc     FSR
        mov     IND, #'5'
        inc     FSR
        mov     IND, #'5'
        inc     FSR
        mov     IND, #'7'

        mov     FSR, #lonASCII   
        mov     IND, #'1'
        inc     FSR
        mov     IND, #'2'
        inc     FSR
        mov     IND, #'1'
        inc     FSR
        mov     IND, #'5'
        inc     FSR
        mov     IND, #'3'
        inc     FSR
        mov     IND, #'.' 
        inc     FSR
        mov     IND, #'3'
        inc     FSR
        mov     IND, #'3'
        inc     FSR
        mov     IND, #'6'
        inc     FSR
        mov     IND, #'5'
        retp

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

subtest

;  test 4x4 subtracter by poking in values, timesing, displaying.

:loop
        call    @delay100ms
        call    @delay100ms
        call    @delay100ms
        call    @delay100ms

        bank    bank_math   

        mov     mult0, #4
        mov     mult1, #0
        mov     mult2, #0
        mov     mult3, #0

        mov     accum0, #2
        mov     accum1, #6
        mov     accum2, #0
        mov     accum3, #0
 
        call    @clearScreen
        call    @lcdLine1 

        bank    bank_math 
        mov     pointer1, #multStart
        call    @showHex32

        call    @sendSpace

        bank    bank_math 
        mov     pointer1, #accumStart
        call    @showHex32

        call    @subtract32    ; mult - accum -> mult

        call    @lcdLine2 
        bank    bank_math 
        mov     pointer1, #multStart
        call    @showHex32

        call    @sendSpace
        bank    bank_math 
        mov     RS232out, negFlag
        call    @sendHexByte

        jmp     @:loop

        retp
 
;-------------------------------------------------------

_multTest

;  test 1x4 multiplier by poking in values, timesing, displaying.

        bank    bank_math     
        mov     math0, #$01 
:loope
        call    @delay100ms
        call    @delay100ms
        call    @delay100ms
        bank    bank_math   
        mov     mult0, #2
        mov     mult1, #0
        mov     mult2, #1
        mov     mult3, #0
        mov     accum0, #0
        mov     accum1, #0
        mov     accum2, #0
        mov     accum3, #0
        inc     math0  
        mov     eight, math0
        call    @clearScreen
        call    @lcdLine1 
        mov     pointer1, #multStart
        call    @showHex32
        call    @sendSpace
        bank    bank_math 
        mov     RS232out, math0
        call    @sendHexByte
        call    @multiply32
        call    @lcdLine2 
        mov     pointer1, #accumStart
        call    @showHex32
        jmp     @:loope
        
        retp

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

_latToBinTest

; test latitude to binary conversion: 37 41.3557 -> 01 59 0E 35

        call    @loadLatLon              ; write test lat + lon into "current"
        call    @clearScreen
        call    @lcdLine1
        call    @showLatLon              ; outputs exactly 20 characters
        mov     pointer1, #latASCII
        call    @DDMMToBin               ; convert to binary in accumulator
        call    @lcdLine2   
        mov     pointer1, #accumStart    ; show what we've got:
        call    @showHex32
        retp

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

_latSubTest

; test latitude subtraction

        call    @loadLatLon              ; write test lat + lon into "current"
        call    @clearScreen
        call    @lcdLine1
        call    @showLatLon              ; outputs exactly 20 characters
        mov     pointer1, #latASCII
        call    @DDMMToBin               ; convert to binary in accumulator
        mov     pointer1, #accumStart
        mov     pointer2, #multStart
        call    @copy32
        bank    bank_math 
        clc
        add     mult0, #1               ; test: tweak one of the numbers: 1 00
        stc
        sub     mult0, #1                ; test other dir: 1 01
        call    @subtract32
        call    @lcdLine2   
        mov     pointer1, #multStart    ; show what we've got:
        call    @showHex32
        call    @sendSpace
        bank    bank_math 
        mov     RS232out, negFlag
        call    @sendHexByte
        retp

;**************************************************************************
; Experimental area 51

_binToASCIITest

; Testing binToASCII
;
; 1. plug known numbers into hiBin, loBin
; 2. run the routine
; 3. send the 5 digit results to the LCD
; 4. pause half a second
; 5. increment the known numbers by 1, 2, 5, ...
; 6. goto 1

        mov     pointer1, #0
        mov     pointer2, #0

:loop
        call    @delay100ms
        call    @delay100ms

        bank    bank_toASCII
        mov     loBin, pointer1
        mov     hiBin, pointer2
        call    @binToASCII
 
        call    @clearScreen
        call    @lcdLine1 

        mov     RS232out, pointer2
        call    @sendHexByte
        mov     RS232out, pointer1
        call    @sendHexByte
        call    @sendSpace

        bank    bank_toASCII
        mov     RS232out, digit5
        call    @sendByte

        bank    bank_toASCII
        mov     RS232out, digit4
        call    @sendByte

        bank    bank_toASCII
        mov     RS232out, digit3
        call    @sendByte

        bank    bank_toASCII
        mov     RS232out, digit2
        call    @sendByte

        bank    bank_toASCII
        mov     RS232out, digit1
        call    @sendByte

        add     pointer1, #1
        jnc     @:loop
        add     pointer2, #1
        jmp     @:loop

        retp
 
;-------------------------------------------------------

_binToASCII

; take 2 bytes (hiBin, loBin) and convert to ASCII digits 65535 - 00000 
; Basically, think of the bytes as the decimal number. 
; For example, if your number is less than 40000 but over 30000,
; then write "3", subtract 30000 from the number, and move on 
; to do the same for the thousands place, the hundreds place, ...
;
; recent test: working perfectly for the first byte 0-255, but
; screws up beyond that.

        bank bank_toASCII

;---------------------------------------- Top of ten thousands loop                        

        mov  counter2, #6
        mov  hiCmp,    #$EA               ; EA60 = 60000
        mov  loCmp,    #$60
:tenThousands
        cjb  hiBin, hiCmp, @:loopOn5      ; if number less than 60000, 50000, ...
        cjb  loBin, loCmp, @:loopOn5      ; then subtract 10000 and test again.
        jmp  @:emitDigit5                 ; otherwise grab the digit and run...
:loopOn5
        sub  loCmp, #$10                  ; $2710 = 10000
        jc   @:p51
        dec  hiCmp
:p51    sub  hiCmp, #$27
        djnz counter2, @:tenThousands
:emitDigit5
        add  counter2, #'0'               ; When we hit the number that this is
        mov  digit5, counter2             ; not less than, then BINGO! the counter
        sub  loBin, loCmp                 ; holds the digit we want! Subtract off
        jc   @:p52
        dec  hiBin
:p52    sub  hiBin, hiCmp                 ; the 10000's place number and continue...

;---------------------------------------  Top of thousands loop

        mov  counter2, #9
        mov  hiCmp,    #$23               ; $2328 = 9000
        mov  loCmp,    #$28
:thousands
        cjb  hiBin, hiCmp, @:loopOn4      ; if this number is less than 9000,
        cjb  loBin, loCmp, @:loopOn4      ; then subtract 1000 and test again.
        jmp  @:emitDigit4
:loopOn4
        sub  loCmp, #$E8                  ; $03E8 = 1000
        jc   @:p41
        dec  hiCmp
:p41    sub  hiCmp, #$03
        djnz counter2, @:thousands
:emitDigit4
        add  counter2, #'0'               ; When we hit the number that this is
        mov  digit4, counter2             ; not less than, then BINGO! the counter
        sub  loBin, loCmp                 ; holds the digit we want!
        jc   @:p42
        dec  hiBin
:p42    sub  hiBin, hiCmp

;---------------------------------------- Top of hundreds loop
 
        mov  counter2, #9
        mov  hiCmp,    #$03               ; $0384 = 900
        mov  loCmp,    #$84
:hundreds
        cjb  hiBin, hiCmp, @:loopOn3      ; if this number is less than 900,
        cjb  loBin, loCmp, @:loopOn3      ; then subtract 100 and test again.
        jmp  @:emitDigit3
:loopOn3
        sub  loCmp, #$64
        jc   @:p31
        dec  hiCmp
:p31  ;  sub  hiCmp, #$00                  ; $0064 = 100
        djnz counter2, @:hundreds
:emitDigit3
        add  counter2, #'0'               ; When we hit the number that this is
        mov  digit3, counter2             ; not less than, then BINGO! the counter
        sub  loBin, loCmp                 ; holds the digit we want!
        jc   @:p32
        dec  hiBin
:p32    sub  hiBin, hiCmp

;---------------------------------------- Top of tens loop

        mov  counter2, #9
        mov  hiCmp,    #$00               ; $005A = 90
        mov  loCmp,    #$5A
:tens
        cjb  hiBin, hiCmp, @:loopOn2      ; if this number is less than 90,
        cjb  loBin, loCmp, @:loopOn2      ; then subtract 10 and test again.
        jmp  @:emitDigit2
:loopOn2
        sub  loCmp, #$A
      ;  jc   @:p21
      ;  dec  hiCmp
:p21 ;   sub  hiCmp, #$00                  ; $000A = 10
        djnz counter2, @:tens
:emitDigit2
        add  counter2, #'0'               ; When we hit the number that this is
        mov  digit2, counter2             ; not less than, then BINGO! the counter
        sub  loBin, loCmp                 ; holds the digit we want!
        jc   @:p22
        dec  hiBin
:p22    sub  hiBin, hiCmp

;---------------------------------------- Top of Ones part

        add  loBin, #'0'      
        mov  digit1, loBin

        retp

;-------------------------------------------------------
;
; some lookup data tables
; like,
;         mov  M, lat_table >> 8    ; put top 3 bits into M
;         mov  W, lat_table         ; put bottom 8 bits into W
;         IREAD                     ; read code memory into W
;         mov  ISR, W               ; and move the result into the variable
          
;-------------------------------------------------------
;org     $600

;lat_table   dw '03741.3557',0
;lon_table   dw '12153.3665',0

; the end
; 
;-------------------------------------------------------
; notes
;
; The GPS sensor has the capability to send maybe a dozen standard
; NMEA (G...) and proprietary Garmin (P...) message strings, like
;
;  $GPGGA,205343,3749.9327,N,12232.3580,W,1,08,1.1,12.5,M,-28.0,M,,*42
;  $PGRMV,0.0,0.0,0.0*5C
;  $PGRMT,GPS 25-LVC VER 2.50 ,P,P,R,R,P,,19,R*15
;
; You program the GPS sensor to configure it to enable the messages you
; want at the baud rate you want, by writing a short ASCII configuration
; string into the sensor's RS232 input wire. 
; Then, whenever the GPS sensor is powered up, it starts emitting groups of 
; all enabled messages, once per second.
; It is up to the user to make sure that you don't enable more characters 
; than may be sent in one second at the programmed baud rate.
; 
; So once its configured, you send power into the GPS sensor, 
; point it at the sky and receive RS232 out a wire. 
;
;-------------------------------------------------------
; The LCD works like this. Send RS232 ASCII charscters and they are
; displayed on the LCD.
; RS232 control characters place the "cursor" at the start of line1, 
; or line2, or anywhere in the displayable area, actually.
; Other control sequences will clear the screen, draw graphics, etc.
;
;---the end----------------------------------------------------


file: /Techref/new/letter/news0312_gpslcd.htm, 60KB, , updated: 2014/10/13 16:01, local time: 2024/11/8 15:46,
TOP NEW HELP FIND: 
3.135.184.127:LOG IN
©2024 PLEASE DON'T RIP! THIS SITE CLOSES OCT 28, 2024 SO LONG AND THANKS FOR ALL THE FISH!

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

<A HREF="http://techref.massmind.org/Techref/new/letter/news0312_gpslcd.htm"> December 2003 MassMind newsletter</A>

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


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

 

Welcome to massmind.org!

 
Quick, Easy and CHEAP! RCL-1 RS232 Level Converter in a DB9 backshell
Ashley Roll has put together a really nice little unit here. Leave off the MAX232 and keep these handy for the few times you need true RS232!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  .