About the Datalink
What is it?
Download Protocol
Display Segments
Memory Map
150 vs 150s
EEProms

Wristapp Programming
Reference
Creating Wristapps
Wristapp Format
The State Table
Wristapp Routines
Wristapps

Wristapp Programming
Tutorials
1 - Hello World
2 - Getting Input
3 - Better Input
4 - Showing Selection
5 - PassWord
[This Page] 6 - Day Find
7 - Playing with Sound
8 - Using Callbacks
9 - Hex Dump
10 - EEPROM Dumper
11 - Spend Watch
12 - Sound Schemes
13 - Random Numbers
14 - Hourly Chimes
15 - Lottery Picker

Sound Schemes
Sound Hardware
Sound Scheme Format

Home Send Mail

Getting time and Input - DAYFIND sample

This is the first real app with some attempt at optimization and a bit of planning for user input.  It stems from a suggestion by Roman Mazi.  There are a lot of things in this code which build on the previous examples.  The most notable things in this one are:

  • This code shows how to get the current date (and you can also get the time the same way).
  • There are banner messages on the bottom of the display to provide a little help.
  • Workarounds for a lack of update routines are given.
  • Quite a few new routines are introduced here.

The code is reasonably commented:

;Name: Day Finder
;Version: DAYFIND
;Description: This will allow you to determine the date for a given day of the week and vice-versa.
;by John A. Toebes, VIII
;
;Press the prev/next buttons to advance by a single day. Press SET to access the ability to advance/backup by
;weeks, months, days, and years.  The MODE button advances through those different states
;
;TIP:  Download your watch faster:  Download a WristApp once, then do not send it again.  It stays in the watch!
;HelpFile: watchapp.hlp
;HelpTopic: 106
            INCLUDE "WRISTAPP.I"
;
; (1) Program specific constants
;
FLAGBYTE        EQU  	$61
B_CLEAR         EQU     0       ; Bit 0 indicates that we need to clear the display first
B_SCANUP        EQU     1       ; Bit 1 indicates that we are scanning up
B_SCANNING      EQU     2       ; Bit 2 indicates that we are in a fake scanning mode
DIGSEL          EQU     $62     ; Indicates which digit we are working on
                                ; 0 = DAY OF WEEK
                                ; 1 = Month
                                ; 2 = Day
                                ; 3 = Year
YEAR_DIG1       EQU     $63     ; This is the first digit of the year to blink (the tens digit)
YEAR_DIG2       EQU     $64     ; This is the second digit of the year to blink (the ones digit)
COUNTER	        EQU     $65     ; A convenient counter for us to advance a week at a time
;
;
; (2) System entry point vectors
;
START   EQU     *
L0110:  jmp     MAIN	; The main entry point - WRIST_MAIN
L0113:  rts             ; Called when we are suspended for any reason - WRIST_SUSPEND
        nop
        nop
L0116:  rts             ; Called to handle any timers or time events - WRIST_DOTIC
        nop
        nop
L0119:  rts             ; Called when the COMM app starts and we have timers pending - WRIST_INCOMM
        nop
        nop
L011c:  rts             ; Called when the COMM app loads new data - WRIST_NEWDATA
        nop
        nop

L011f:  lda     STATETAB0,X ; The state table get routine - WRIST_GETSTATE
        rts

L0123:  jmp     HANDLE_STATE0
        db      STATETAB0-STATETAB0
L0127:  jmp     HANDLE_STATE1
        db      STATETAB1-STATETAB0
;
; (3) Program strings
S6_DAY          timex6  "DAY "
S6_FIND         timex6  "  FIND"
S8_TOEBES       timex   "J.TOEBES"
S8_DAYFIND      timex   "DAY FIND"
S8_WEEK         db      C_LEFTARR
                timex   " WEEK "
                db      C_RIGHTARR
S8_MONTH        db      C_LEFTARR
                timex   "MONTH "
                db      C_RIGHTARR
S8_DAY          db      C_LEFTARR
                timex   " DAY  "
                db      C_RIGHTARR
S8_YEAR         db      C_LEFTARR
                timex   " YEAR "
                db      C_RIGHTARR
;
; (4) State Table
;
STATETAB0:
        db      0
        db      EVT_ENTER,TIM1_4TIC,0           ; Initial state
        db      EVT_TIMER1,TIM_ONCE,0           ; The timer from the enter event
        db      EVT_RESUME,TIM_ONCE,0           ; Resume from a nested app
        db      EVT_MODE,TIM_ONCE,$FF           ; Mode button
        db      EVT_SET,TIM_ONCE,1              ; SET button pressed
        db      EVT_DNNEXT,TIM2_8TIC,0          ; NEXT button pressed
        db      EVT_DNPREV,TIM2_8TIC,0          ; PREV button pressed
        db      EVT_UPANY4,TIM_ONCE,0           ; The
        db      EVT_TIMER2,TIM2_TIC,0           ; The timer for the next/prev button pressed
        db      EVT_END

STATETAB1:
        db      1
        db      EVT_RESUME,TIM_ONCE,1           ; Resume from a nested app
        db      EVT_DNANY4,TIM_ONCE,1           ; NEXT, PREV, SET, MODE button pressed
        db      EVT_UPANY4,TIM_ONCE,1           ; NEXT, PREV, SET, MODE button released
        db      EVT_USER2,TIM_ONCE,0
        db      EVT_USER3,TIM2_8TIC,1           ;
        db      EVT_TIMER2,TIM2_TIC,1           ;
        db      EVT_END
;
; (5) State Table 0 Handler
; This is called to process the state events.
; We see ENTER, TIMER2, and RESUME events
;
HANDLE_STATE0:
        bset    1,APP_FLAGS	                ; Indicate that we can be suspended
        lda     BTNSTATE                        ; Get the event
        cmp     #EVT_DNNEXT
        beq     DO_NEXT0
        cmp     #EVT_DNPREV
        beq     DO_PREV0
        cmp     #EVT_TIMER2
        beq     DO_SCAN
        cmp     #EVT_ENTER                      ; Is this our initial entry?
        bne     REFRESH0
;
; This is the initial event for starting us up
;
DO_ENTER    
;
; (6) This code gets the current date from the system

        jsr     ACQUIRE                         ; Lock so that it doesn't change under us
        ldx     #TZ1_MONTH                      ; Assume that we are using the first timezone
        jsr     CHECK_TZ                        ; See which one we are really using
        bcc     COPY_TZ1                        ; If we were right, just skip on to do the work
        ldx     #TZ2_MONTH                      ; Wrong guess, just load up the second time zone
COPY_TZ1
        lda     0,x                             ; Copy out the month
        sta     SCAN_MONTH
        lda     1,x                             ; Day
        sta     SCAN_DAY
        lda     2,x                             ; and year
        sta     SCAN_YEAR
        jsr     RELEASE                         ; Unlock so the rest of the system is happy

        bclr    B_CLEAR,FLAGBYTE                ; Indicate that we need to clear the display
        clr     DIGSEL                          ; Start us off on the week advance
        jsr     CLEARSYM                        ; Clear the display
        lda     #S6_DAY-START
        jsr     PUT6TOP
        lda     #S6_FIND-START
        jsr     PUT6MID
        lda     #S8_TOEBES-START
        jmp     BANNER8

DO_SCAN
        brclr   B_SCANUP,FLAGBYTE,DO_PREV0      ; Were we scanning up or down?
DO_NEXT0
        bset    B_SCANUP,FLAGBYTE               ; We are now scanning up
        jsr     INCREMENT_SCAN_DATE             ; Advance to the next date
        bra     SHOW_DATE                       ; Comment this out and use the next one if you want
        ;  jmp     APPT_SHOW_SCAN               ; to put the text 'SCAN' on the bottom when we are in scan mode

DO_PREV0
        bclr    B_SCANUP,FLAGBYTE               ; We are now scanning down
        jsr     DECREMENT_SCAN_DATE             ; Back up to the previous date
        bra     SHOW_DATE                       ; Show the date on the screen.
        ;  jmp     APPT_SHOW_SCAN               ; Use this if you want 'SCAN' on the bottom of the display
;
; We come here for a RESUME or TIMER2 event.  For this we want to reset the display
;
REFRESH0
        brset   B_CLEAR,FLAGBYTE,NOCLEAR0       ; Do we need to clear the display first?
        bset    B_CLEAR,FLAGBYTE                ; Mark that the display has been cleared
        jsr     CLEARALL                        ; and do the work of clearing
NOCLEAR0
        lda     #S8_DAYFIND-START               ; Put up the name of the app on the display
        jsr     BANNER8
SHOW_DATE
        jsr     APPT_SHOW_DATE                  ; Show the date on the screen
        ldx     SCAN_YEAR                       ; as well as the year
        jmp     PUTYEARMID
;--------------------------------------------------------------------------------
; (7) State Table 1 Handler
; This is called to process the state events.
; We see SET, RESUME, USER3, TIMER2, DNANY4, and UPANY4 events
;  We use the USER3 to trigger a delay which fires off a TIMER2 sequence of events.
;  This allows us to have the PREV/NEXT buttons repeat for advancing the WEEK and YEAR
;  since we can't use the UPDATE routines for them.
;
HANDLE_STATE1:
        bset    1,APP_FLAGS	                ; Indicate that we can be suspended
        lda     BTNSTATE                        ; Get the event
        cmp     #EVT_TIMER2                     ; Was it a timer for a repeat operation?
        beq     DO_UPD                          ; Yes, go handle it
        cmp     #EVT_USER3                      ; Was it the USER3 event fired from the PREV/NEXT buttons?
        bne     TRY_UP                          ; No, try again
        rts                                     ; Yes, just ignore it, it will cause a timer to go off later
TRY_UP
        bclr    B_SCANNING,FLAGBYTE             ; We can't be scanning any more, so turn it off
        cmp     #EVT_UPANY4                     ; Was it any button being released?
        bne     TRY_DN                          ; No, try again
        jmp     REFRESH                         ; Yes, go refresh the screen (note that the branch is out of range)
TRY_DN
        cmp     #EVT_DNANY4                     ; Is this our initial entry?
        beq     GET_DN                          ; No, try again
        jmp     FORCEFRESH                      ; Yes, go setup the screen (note that the branch is out of range)
GET_DN
        lda     BTN_PRESSED                     ; Let's see what the button they pressed was
        cmp     #EVT_PREV                       ; How about the PREV button
        beq     DO_PREV                         ; handle it
        cmp     #EVT_NEXT                       ; Maybe the NEXT button?
        beq     DO_NEXT                         ; Deal with it!
        cmp     #EVT_MODE                       ; Perhaps the MODE button
        beq     DO_MODE                         ; If so, handle it
        ; It must be the set button, so take us out of this state
        lda     #EVT_USER2
        jmp     POSTEVENT
;
; (8) Our real working code...
; We come here when they press the next/prev buttons.  if we are in a timer repeat
; situation (triggered when they press prev/next for the WEEK/YEAR) then we skip right
; to processing based on the button that was previously pressed
;
DO_NEXT
        bset    0,SYSFLAGS                      ; Mark our update direction as up
        bra     DO_UPD
DO_PREV
        bclr    0,SYSFLAGS                      ; Mark our update direction as down
DO_UPD
        lda     DIGSEL                          ; Which digit mode are we in?
        beq     DO_UPD_DOW                      ; 0 - Handle the WEEK
        cmp     #2
        blo     DO_UPD_MONTH                    ; <2 = 1 - Handle the MONTH
        beq     DO_UPD_DAY                      ; 2 - Handle the Day
DO_UPD_YEAR                                     ; >2 = 3 - Handle the YEAR
        brclr   0,SYSFLAGS,LASTYEAR             ; Were we in the down direction?
        ldx     #99                             ; Going up, let the WRAPX routine handle it for us
        lda     SCAN_YEAR
        jsr     INCA_WRAPX
        bra     SAVEYEAR
LASTYEAR
        lda     SCAN_YEAR                       ; Going down, get the year
        deca                                    ; Decrement it
        bpl     SAVEYEAR                        ; and see if we hit the lower end
        lda     #99                             ; Yes, 2000 wraps down to 1999
SAVEYEAR
        sta     SCAN_YEAR                       ; Save away the new year
        bra     SETUP_LAG                       ; And fire off an event to allow for repeating

DO_UPD_DOW                                      ; 0 - Day of week
        lda     #7                              ; We want to iterate 7 times advancing by one day.
        sta     COUNTER                         ;  (this makes it much easier to handle all the fringe cases)
WEEKLOOP
        brclr   0,SYSFLAGS,LASTWEEK             ; Are we going backwards?
        jsr     INCREMENT_SCAN_DATE             ; Going forwards, advance by one day
        bra     WEEKLOOPCHK                     ; And continue the loop
LASTWEEK
        jsr     DECREMENT_SCAN_DATE             ; Going backwards, retreat by one day
WEEKLOOPCHK
        dec     COUNTER                         ; Count down
        tst     COUNTER                         ; See if we hit the limit
        bne     WEEKLOOP                        ; and go back for more
; (9) Fake repeater
; This code is used for the Day of week and year modes where we want to have a
; repeating button, but the system routines won't handle it for us
; It works by posting a USER3 event which has a timer of about 1/2 second.
; After that timer expires, we get a timer2 event which then repeats every tic.
; The only thing that we have to worry about here is to not go through this
; every time so that it takes 1/2 second for every repeat.
SETUP_LAG
        brset   B_SCANNING,FLAGBYTE,INLAG       ; If we were already scanning, skip out
        bset    B_SCANNING,FLAGBYTE             ; Indicate that we are scanning
        lda     #EVT_USER3                      ; and post the event to start it off
        jsr     POSTEVENT
INLAG
        jmp     SHOW_DATE                       ; Put the date up on the display
; (10) Update routine usage
DO_UPD_MONTH                                    ; 1 - Handle the month
        lda     #MONTH_JAN                      ; The bottom end is January
        sta     UPDATE_MIN
        lda     #MONTH_DEC                      ; and the top end is December (INCLUSIVE)
        sta     UPDATE_MAX
        lda     #UPD_HMONTH                     ; We want the HALF-MONTH udpate function
        ldx     #SCAN_MONTH                     ; To update the SCAN_MONTH variable
        bra     SEL_UPD                         ; Go do it
DO_UPD_DAY                                      ; 2 - Handle the day
        lda     #1                              ; 1 is the first day of the month
        sta     UPDATE_MIN
        jsr     GET_SCAN_MONTHLEN               ; Figure out how long the month is
        sta     UPDATE_MAX                      ; and make that the limit
        lda     #UPD_HDAY                       ; We want the HALF-DAY update function
        ldx     #SCAN_DAY                       ; to update the SCAN_DAY variable
SEL_UPD
        jsr     START_UPDATEP                   ; And prepare the update routine
        bset    4,BTNFLAGS                      ; Mark that the update is now pending
        rts
; (11) Making the mode button work
; when they press the mode button, we want to cycle through the various choices
; on the display.
DO_MODE
        lda     DIGSEL                          ; Figure out where we are in the cycle
        inca                                    ; advance to the next one
        and     #3                              ; and wrap at 4 to zero
        sta     DIGSEL
REFRESH
        brset   B_CLEAR,FLAGBYTE,NOCLEAR        ; Do we need to clear the display first?
FORCEFRESH
        jsr     CLEARALL                        ; Yes, clear everything before we start
        bset    B_CLEAR,FLAGBYTE                ; And remember that we have already done that
NOCLEAR
        clr     BTNFLAGS                        ; Turn off any scrolling banners
        lda     #ROW_TD23                       ; Turn off the dash from the week blink
        sta     DISP_ROW
        bclr    COL_TD23,DISP_COL
        jsr     SHOW_DATE                       ; Display the date
; (12) Establishing a blink routine
; This makes the appropriate section of the display blink based on what we are changing
        lda     DIGSEL                          ; Get the digit we are on
        beq     DO_BLINK_DOW                    ; 0 -> Update Day of week
        cmp     #2                              
        blo     DO_BLINK_MONTH                  ; <2 = 1 -> Update month
        beq     DO_BLINK_DAY                    ; 2 - Update day of month

DO_BLINK_YEAR   ;        3: Year
; (13) Calling BLINK_SECOND
; For BLINK_SECONDS, the UPDATE_PARM points to the 2 character format for the year.
        ldx     SCAN_YEAR                       ; Get our year
        jsr     GETBCDHI                        ; And extract out the high digit of it
        sta     YEAR_DIG1                       ; Save that away
        ldx     SCAN_YEAR                       ; Do it again
        jsr     GETBCDLOW                       ; to get the low digit
        sta     YEAR_DIG2                       ; and save that away
        ldx     #YEAR_DIG1                      ; the parm points to the first digit
        lda     #BLINK_SECONDS                  ; and we want a BLINK_SECONDS function
        bra     SETUP_BLINK                     ; so do it already

DO_BLINK_DOW    ;        0: Day of week:
; (14) Calling BLINK_SEGMENT
; Unfortunately, there is no blink routine to blink the upper two letters on the display.
; To get around this, I have chosen to blink a single segment on the display (the dash
; after the day of the week).  This routine was designed to blink the AM/PM or other
; symbols, but it works quite fine for our purposed.  You need to set UPDATE_POS to have
; the row to be updated and UPDATE_VAL holds the mask for the COLUMS to be XORed.
; In this way, you might have more than one segment blinking, but there are few segments
; on the same row which would achieve a reasonable effect. 
;            UPDATE_POS   ROW_TD23 
;            UPDATE_VAL   (1<<COL_TD23)
        lda     #ROW_TD23
; We want to blink the DASH after the day of week sta UPDATE_POS
; Store the ROW for it in UPDATE_POS lda #(1<<COL_TD23)
; Get the mask for the column sta UPDATE_VAL
; And store that in UPDATE_VAL lda #BLINK_SEGMENT
; We want a BLINK_SEGMENT function bra SETUP_BLINK
; and get to it.
DO_BLINK_MONTH         ; 1: Month
; (15) Calling BLINK_HMONTH, BLINK_HDAY
; These are the normal boring cases of calling the blink routine.  They simply need the
; address of the byte holding the value to blink and the function to blink them with.
;            UPDATE_PARM - Points to the month
        lda     #BLINK_HMONTH                   ; We want a BLINK HALF-MONTH function
        ldx     #SCAN_MONTH                     ; to blink our month
        bra     SETUP_BLINK                     ; and do it

DO_BLINK_DAY    ;       2: Day
;           UPDATE_PARM - Points to the day
        lda     #BLINK_HDAY                     ; We want a BLINK HALF-DAY function
        ldx     #SCAN_DAY                       ; to blink our day

SETUP_BLINK
        jsr     START_BLINKP                    ; Request the blink function
        lda     digsel                          ; Figure out which one we are blinking
        lsla                                    ; *2
        lsla                                    ; *4
        lsla                                    ; *8
        add     #S8_WEEK-START                  ; And use that to index the banner to put on the bottom
        jsr     BANNER8
        bset    2,BTNFLAGS                      ; Mark a blink routine as pending
        rts
;
; (16) This is the main initialization routine which is called when we first get the app into memory
;
MAIN:
        lda     #$c0	                        ; We want button beeps and to indicate that we have been loaded
        sta     WRISTAPP_FLAGS
        clr     FLAGBYTE                        ; start with a clean slate
        rts

This is code is built on the passwd with a quite a few changes and additions.

  1. Program specific constants - different uses for the flags and a couple of new local variables 
  2. System entry point vectors - No change here.
  3. Program strings - Gee, we changed the strings.  Note the four strings in a row which serve as help messages when in set mode.
  4. State Table(s) - State table0 is not radically changed (We added the next/prev buttons).  State table 1 is used when we are in the set mode.  See The State Table for a more complete explaination of this.  Note the use of the USER3 event in this table
  5. State Table Handler0 - For state0, we only really need to handle the initial enter where we put up the banner.  After a while we time out and put up the current day of the week and our banner.
  6. Get the system date - This shows how to get the current date.
  7. State table 1 handler
  8. Program Specific Code - We use the same UPDATE and BLINK functions from the Blink sample.
  9. Fake Repeater - I'm pretty proud of this one...
  10. Update routine usage - Look here for some clues on using the update routines.
  11. Making the mode button work
  12. Establishing a blink routine
  13. Calling BLINK_SECOND
  14. Calling BLINK_SEGMENT
  15. Calling BLINK_HMONTH, BLINK_HDAY
  16. Main initialization - Surprosingly, there is not much change here.
1