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

Wristapp Programming
Creating Wristapps
Wristapp Format
The State Table
Wristapp Routines

Wristapp Programming
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



L0116:  rts             ; Called to handle any timers or time events - WRIST_DOTIC



L0119:  rts             ; Called when the COMM app starts and we have timers pending - WRIST_INCOMM



L011c:  rts             ; Called when the COMM app loads new data - WRIST_NEWDATA



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


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



        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


        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



        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




; (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


        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


        brclr   B_SCANUP,FLAGBYTE,DO_PREV0      ; Were we scanning up or down?


        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


        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



        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


        lda     #S8_DAYFIND-START               ; Put up the name of the app on the display

        jsr     BANNER8


        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.



        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


        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)


        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)


        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



        bset    0,SYSFLAGS                      ; Mark our update direction as up

        bra     DO_UPD


        bclr    0,SYSFLAGS                      ; Mark our update direction as down


        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


        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


        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)


        brclr   0,SYSFLAGS,LASTWEEK             ; Are we going backwards?

        jsr     INCREMENT_SCAN_DATE             ; Going forwards, advance by one day

        bra     WEEKLOOPCHK                     ; And continue the loop


        jsr     DECREMENT_SCAN_DATE             ; Going backwards, retreat by one day


        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.


        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


        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


        jsr     START_UPDATEP                   ; And prepare the update routine

        bset    4,BTNFLAGS                      ; Mark that the update is now pending


; (11) Making the mode button work

; when they press the mode button, we want to cycle through the various choices

; on the display.


        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


        brset   B_CLEAR,FLAGBYTE,NOCLEAR        ; Do we need to clear the display first?


        jsr     CLEARALL                        ; Yes, clear everything before we start

        bset    B_CLEAR,FLAGBYTE                ; And remember that we have already done that


        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


; 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


        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



; (16) This is the main initialization routine which is called when we first get the app into memory



        lda     #$c0	                        ; We want button beeps and to indicate that we have been loaded

        sta     WRISTAPP_FLAGS

        clr     FLAGBYTE                        ; start with a clean slate


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
  16. Main initialization - Surprosingly, there is not much change here.