;registers used
; R0 = byte read from serial port
; R1 = GPS output field number (1-15)
; R2 = GPS field character number
; R3 = timer routine during LCD initialization, minute timer
; R4 = timer routine during LCD initialization, minute timer
; R5 = timer routine during LCD initialization, minute timer
; R6 = character read from serial port
; R7 = temp storage for character while writing to LCD

;register addresses not already defined
        MCON  EQU  0C6h
        TA    EQU  0C7h

;reset interrupt service routine
        ORG   0000h
        SJMP  start

;GPS interrupt service routine
        ORG   0023h
        JMP   GPS

;power fail warning interrupt service routine (just wait to die)
        ORG   002Bh
        SJMP  $
        RETI

;--------------------------------------------------------------------------------
                                   ;main routine

        ORG   0030h
start:
                                   ;Set memory partition

        MOV   TA,   #0AAh          ;Timed...
        MOV   TA,   #055h          ; ...Access
        ORL   MCON, #02h           ;Set PAA
        MOV   MCON, #020h          ;Set partition to 4KB
        MOV   TA,   #0AAh          ;Timed...
        MOV   TA,   #055h          ; ...Access
        ANL   MCON, #0FDh          ;Clear PAA

                                   ;Move data pointer to first unused RAM location

        MOV   DPTR, #1000h         ;Start of data memory
iram:   MOVX  A,@DPTR              ;Read what's at the current data memory location
        CJNE  A,#0B7H,used         ;If it's not the predefined value B7H, it's used
        SJMP  unused               ;If we get here, the location is unused and we're done
used:   INC   DPTR                 ;If we get here, current position's used, go to next
        SJMP  iram
unused:

                                   ;Turn on interrupts

        ORL   PCON, #08h           ;Enable Power-fail warning
        SETB  PS                   ;Set serial port interrupt to high priority
        SETB  ES                   ;Enable serial port interrupt
        CLR   PT0                  ;Set external interrupt 0 to low priority
        CLR   EX0                  ;Disable external interrupt 0 (pin 3.2)
        SETB  EA                   ;Turn on interrupts
                                   ;Danger, may want to turn on interrupts after
                                   ;initialize LCD

                                   ;Set up serial port

        MOV   SCON, #01010000b     ;Select mode 1 (async, 8N1, baud from timer 1)
        MOV   TH1,  #245           ;Baud rate (245 -> 2400 baud)
        ANL   PCON, #07fh          ;Baud rate multiplier (set to 1)
        MOV   TMOD, #00100000b     ;Set timer 1 for 8-bit auto-reload mode
        MOV   TCON, #01000000b     ;Enable timer 1
        CLR   RI                   ;Clear serial receive flag
        CLR   TI                   ;Clear serial transmit flag

                                   ;Initialize radiation pulse counter

        MOV   2BH,#48              ;Count stored in 5 bytes, ones place
        MOV   2CH,#48              ;tens place
        MOV   2DH,#48              ;hundreds place
        MOV   2EH,#48              ;thousands place
        MOV   2FH,#48              ;ten thousands place

                                   ;LCD initialization

        MOV   A,20                 ;wait > 15ms
        ACALL waitms
        MOV   A,#00011000b
        ACALL wlcd
        MOV   A,5                  ;Wait > 4.1ms
        ACALL waitms
        MOV   A,#00011000b
        ACALL wlcd
        MOV   A,100                ;Wait > 100us
        ACALL waitus
        MOV   A,#00011000b
        ACALL wlcd
        MOV   A,5                  ;Wait > 4.1ms
        ACALL waitms
        MOV   A,#00010000b         ;Set 4 bit operation, interface still 8 bit
        ACALL wlcd
        MOV   A,#00010000b         ;Interface 4 bit, set number of lines and font
        ACALL wlcd
        MOV   A,#01000000b
        ACALL wlcd
        MOV   A,#00000000b         ;Turn display off
        ACALL wlcd
        MOV   A,#01000000b
        ACALL wlcd
        MOV   A,#00000000b         ;Turn display on
        ACALL wlcd
        MOV   A,#01100000b
        ACALL wlcd
        MOV   A,#00000000b         ;Clear display
        ACALL wlcd
        MOV   A,#00001000b
        ACALL wlcd
        MOV   A,5
        ACALL waitms
        MOV   A,#00000000b         ;Set entry mode to increment cursor w/o shift
        ACALL wlcd
        MOV   A,#00110000b
        ACALL wlcd

        MOV   R1,#1                ;First field
        MOV   R2,#1                ;First character

        MOV   A,#01000000b         ;Set LCD address to character position 0
        ACALL wlcd
        MOV   A,#00000000b
        ACALL wlcd

;--------------------------------------------------------------------------------
                                   ;Geiger counter routine

                                   ;This routine loops for up to slightly more than
                                   ;1 minute counting radiation pulses.  If there is
                                   ;no GPS receiver the radiation count will be written
                                   ;out at this interval.  If there is a GPS receiver
                                   ;it will cause data to be written every minute restarting
                                   ;the following loops before they cause the output.
                                   ;This way, data is output about every minute whether
                                   ;or not the GPS receiver is attached.

Geiger: 
         MOV   R3,#200             ;this set of loops gives a slightly > 1 min. integration
gloop1:  MOV   R4,#255
gloop2:  MOV   R5,#255
gloop3:  JNB   P3.2,nopul          ;check for a pulse
         ACALL pulse
nopul:   DJNZ  R5,gloop3
         DJNZ  R4,gloop2
         DJNZ  R3,gloop1
         ACALL wgeig               ;if no GPS, write geiger output here
         MOV   R0,#13
         ACALL save
         SJMP  Geiger              ;Go back and integrate for another minute

pulse:   INC   2BH                 ;Increment the pulse counter
         MOV   A,2BH
         CJNE  A,#58,nocarry
         MOV   2BH,#48
         INC   2CH
         MOV   A,2CH
         CJNE  A,#58,nocarry
         MOV   2CH,#48
         INC   2DH
         MOV   A,2DH
         CJNE  A,#58,nocarry
         MOV   2DH,#48
         INC   2EH
         MOV   A,2EH
         CJNE  A,#58,nocarry
         MOV   2EH,#48
         INC   2FH
nocarry: JB    P3.2,$              ;Wait for geiger-mueller dead time to end
         RET

;--------------------------------------------------------------------------------
                                   ;LCD update routine

LCD:
        MOV   R7,A
        RR    A
        ANL   A,#01111000b
        ORL   A,#00000001b
        ACALL wlcd
        MOV   A,R7
        RL    A
        RL    A
        RL    A
        ANL   A,#01111000b
        ORL   A,#00000001b
        ACALL wlcd
        MOV   A,R7
        RET

;--------------------------------------------------------------------------------
                                   ;GPS/data storage routine

GPS:    MOV   R0,SBUF
        JB    RI,good
        CLR   RI
        CLR   TI
        RETI

good:   CLR   RI
        CLR   TI

        CJNE  R0,#36,chkcom        ;If the character is a $, at start so reset field
        MOV   R1,#1
        ACALL wgeig                ;Output the radiation count and zero counters
        RETI

chkcom: CJNE  R0,#44,field1        ;If the character is a comma, we have a new field
        ACALL comma
        RETI

field1: CJNE  R1,#1,field2         ;field 1 is the unused key phrase
        RETI

field2: CJNE  R1,#2,field3         ;field 2 is the time
        ACALL save                 ;all time bytes get stored
hhmm:   CJNE  R2,#3,mmss           ;put a colon between the hour and minute...
        MOV   A,#58
        ACALL LCD
mmss:   CJNE  R2,#5,time           ;...or put a colon between the minute and second
        MOV   A,#58
        ACALL LCD
time:   MOV   A,R0                 ;...then/or display the number
        ACALL LCD
        INC   R2                   ;another character done
        RETI

field3: CJNE  R1,#3,field4         ;field 3 is the latitude
        ACALL save                 ;all latitude bytes get stored
        CJNE  R2,#3,lat            ;write degree symbol if the right spot...
        MOV   A,#0DFH
        ACALL LCD
lat:    MOV   A,R0                 ;...then/or display the number...
        ACALL LCD
        CJNE  R2,#9,didlat         ;...plus the minute tick if the right spot
        MOV   A,#39
        ACALL LCD
didlat: INC   R2                   ;another character done
        RETI

field4: CJNE  R1,#4,field5         ;field 4 is always "N"
        MOV   A,#78
        ACALL LCD
        RETI

field5: CJNE  R1,#5,field6         ;field 5 is the longitude
        ACALL save                 ;all longitude bytes get stored
        CJNE  R2,#4,lon            ;write degree symbol if the right spot...
        MOV   A,#0DFH
        ACALL LCD
lon:    MOV   A,R0                 ;...then/or display the number...
        ACALL LCD
        CJNE  R2,#10,didlon        ;...plus the minute tick if the right spot
        MOV   A,#39
        ACALL LCD
didlon: INC   R2                   ;another character done
        RETI

field6: CJNE  R1,#6,field7         ;field 6 is always "W"
        MOV   A,#87
        ACALL LCD
        RETI

field7: CJNE  R1,#7,field8         ;field 7 is unused quality number
        RETI

field8: CJNE  R1,#8,field9         ;field 8 is the number of satellites
        ACALL save                 ;all characters of this field get saved
        MOV   A,R0                 ;all characters get written to the LCD too
        ACALL LCD
        CJNE  R2,#2,didsat         ;after second number, write units
        MOV   A,#32
        ACALL LCD
        MOV   A,#115
        ACALL LCD
        MOV   A,#97
        ACALL LCD
        MOV   A,#116
        ACALL LCD
        MOV   A,#115
        ACALL LCD
didsat: INC   R2                   ;another character done
        RETI

field9: CJNE  R1,#9,fielda         ;field 9 is the unused hdop
        RETI

fielda: CJNE  R1,#10,fieldb        ;field 10 is the altitude
        ACALL save                 ;all characters of this field get saved
;        CJNE  R0,#46,chkalt        ;we won't write the decimal or digits after it
;        MOV   A,#109               ;write units
;        ACALL LCD
;        MOV   R2,#18               ;set flag so wont do next character
;        RETI
;chkalt: CJNE  R2,#18,doalt         ;if decimal number, just return
;        RETI
doalt:  MOV   A,R0                 ;write the number
        CALL  LCD
        RETI

fieldb: CJNE  R1,#11,fieldc        ;field 11 is the altitude units (always "m")
        RETI

fieldc: CJNE  R1,#12,fieldd        ;field 12 is the unused geoid altitude
        RETI

fieldd: CJNE  R1,#13,fielde        ;field 13 is the unused geoid units
        RETI

fielde: CJNE  R1,#14,fieldf        ;field 14 is the differential age
        RETI

fieldf: CJNE  R0,#13,back          ;field 15 contains the <CR>, output it to memory
        CALL  save
        RETI

back:   RETI



;--------------------------------------------------------------------------------
                                   ;Increments the field count and cursor
                                   ;position when we get a comma from the GPS

comma:  INC   R1                   ;Number of next field
        MOV   R2,#1                ;Set pointer to point at first char of field

        CJNE  R1,#1,char2          ;$GPGGA, unused
        RET

char2:  CJNE  R1,#2,char3          ;time
        MOV   A,#01000000b
        ACALL wlcd
        MOV   A,#00000000b
        ACALL wlcd
        RET

char3:  CJNE  R1,#3,char4          ;latitude
        MOV   A,#01100000b
        ACALL wlcd
        MOV   A,#00001000b
        ACALL wlcd
        RET

char4:  CJNE  R1,#4,char5          ;N or S
        MOV   A,#01100000b
        ACALL wlcd
        MOV   A,#01101000b
        ACALL wlcd
        RET

char5:  CJNE  R1,#5,char6          ;longitude
        MOV   A,#01001000b
        ACALL wlcd
        MOV   A,#00000000b
        ACALL wlcd
        RET

char6:  CJNE  R1,#6,char7          ;E or W
        MOV   A,#01001000b 
        ACALL wlcd
        MOV   A,#01101000b
        ACALL wlcd
        RET

char7:  CJNE  R1,#7,char8          ;quality, unused
        RET

char8:  CJNE  R1,#8,char9          ;number of satellites
        MOV   A,#01000000b
        ACALL wlcd
        MOV   A,#01001000b
        ACALL wlcd
        RET

char9:  CJNE  R1,#9,chara          ;HDOP, unused
        RET

chara:  CJNE  R1,#10,charb         ;altitude
        MOV   A,#01101000b
        ACALL wlcd
        MOV   A,#00000000b
        ACALL wlcd
;        MOV   A,#32                ;1st, clear the field
;        ACALL LCD
;        ACALL LCD
;        ACALL LCD
;        ACALL LCD
;        ACALL LCD
;        MOV   A,#01101000b         ;move back to the start of the field
;        ACALL wlcd
;        MOV   A,#00000000b
;        ACALL wlcd
        RET

charb:  CJNE  R1,#11,charc         ;altitude units
;        MOV   A,#01101000b
;        ACALL wlcd
;        MOV   A,#00100000b
;        ACALL wlcd
        RET

charc:  CJNE  R1,#12,chard         ;geoid height, unused
        RET

chard:  CJNE  R1,#13,chare         ;geoid units, unused
        RET

chare:  CJNE  R1,#14,charf         ;diff GPS age, unused
        RET

charf:  CJNE  R1,#15,badf
        RET

badf:   RET

;--------------------------------------------------------------------------------
                                   ;Writes the radiation count

wgeig:  MOV   A,#01101000b         ;set the location on the LCD
        ACALL wlcd
        MOV   A,#00110000b
        ACALL wlcd

        MOV   A,2FH                ;ten thousands place
        ACALL LCD
        MOV   R0,2FH
        ACALL save
        MOV   A,2EH                ;thousands place
        ACALL LCD
        MOV   R0,2EH
        ACALL save
        MOV   A,2DH                ;hundreds place
        ACALL LCD
        MOV   R0,2DH
        ACALL save
        MOV   A,2CH                ;tens place
        ACALL LCD
        MOV   R0,2CH
        ACALL save
        MOV   A,2BH                ;ones place
        ACALL LCD
        MOV   R0,2BH
        ACALL save
        MOV   A,#0E4H              ;mu
        ACALL LCD
        MOV   A,#82                ;R
        ACALL LCD
        MOV   A,#47                ;/
        ACALL LCD
        MOV   A,#104               ;h
        ACALL LCD
        MOV   A,#114               ;r
        ACALL LCD

        MOV   2BH,#48              ;reset radiation pulse counter
        MOV   2CH,#48
        MOV   2DH,#48
        MOV   2EH,#48
        MOV   2FH,#48
  
        MOV   R3,#200              ;reset integration timeout loop counters
        MOV   R4,#255
        MOV   R5,#255

        RET

;--------------------------------------------------------------------------------
                                   ;Save a byte to memory
save:   MOV   A,R0
        MOVX  @DPTR,A
        INC   DPTR
        RET

;--------------------------------------------------------------------------------
                                   ;Millisecond delay routine, waits ACC ms

waitms: DEC   A                    ;Note, if A=1, may fail
        MOV   R3,A
loopm1: MOV   R4,#2                ;2*208 loops approx 1ms
loopm2: MOV   R5,#208
        DJNZ  R5,$
        DJNZ  R4,loopm2
        DJNZ  R3,loopm1           ;ACC=number of milliseconds to wait

        RET

;--------------------------------------------------------------------------------
                                   ;Microsecond delay routine, waits ACC us

waitus: DEC   A                    ;Note, if A=1, may fail
        DJNZ  ACC,$                ;each loop is 2.4 us (so will get 2.4x time requested)
        RET

;--------------------------------------------------------------------------------
                                   ;Write byte to LCD

wlcd:   MOV   P0,A                 ;Initial setup

        SETB  P0.2                 ;Take E high
        CLR   P0.2                 ;Take E low

        MOV   A,120                ;Wait 120us
        ACALL waitus

        RET
;--------------------------------------------------------------------------------
        END
