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
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
[This Page] 15 - Lottery Picker

Sound Schemes
Sound Hardware
Sound Scheme Format

Home Send Mail

More Random Numbers and Marquis - PICK6 example

Philip Hudnott <Philip.hudnott@btinternet.com> came up with this idea for a wristapp to pick lottery numbers. Overall, this is pretty simple wristapp to write, but it really showed the need for a decent random number generator. Fortunately, Alan Beale <biljir@pobox.com> provided me with a great MWC (multiply-with-carry) algorithm. Feel free to use the random number generator for other programs, it has some pretty good behaviour. Overall, this program has very little changes from the 3BALL example, so getting into it should be pretty easy.

;Name: PICK6
;Version: PICK6
;Description: A sample lottery number picker to pick 6 numbers out of a pool of 49 numbers (no duplicates allowed).
;  To use it, just select it as the current app and it will pick a set of 6 numbers for you.  To get another set,
;  just press the next button.  This is for amusement only (but if you win anything because of it, I would welcome
;  anything that you send me).
;
;by John A. Toebes, VIII
;
;HelpFile: watchapp.hlp
;HelpTopic: 100
;****************************************************************************************
;* Copyright (C) 1997 John A. Toebes, VIII                                              *
;* All Rights Reserved                                                                  *
;* This program may not be distributed in any form without the permission of the author *
;*         jtoebes@geocities.com                                                        *
;****************************************************************************************
; (1) Program specific constants
;
            INCLUDE "WRISTAPP.I"
;
; Program specific constants
;
RAND_RANGE      EQU     48      ; This is the number of items to select from (1 to RAND_RANGE+1)
CURRENT_TIC     EQU     $27     ; Current system clock tic (Timer)
RAND_WCL        EQU     $61
RAND_WCH        EQU     $62
RAND_WNL        EQU     $63
RAND_WNH        EQU     $64
THIS_PICK       EQU     $65     ; We can share this with MARQ_POS since we don't do both at the same time
MARQ_POS        EQU     $65
TEMPL           EQU     $66
TEMPH           EQU     $67
START	        EQU   *
BASE_TAB        EQU     $FE
;
; (2) System entry point vectors
;
L0110:  jmp     MAIN	; The main entry point - WRIST_MAIN
L0113:  bclr    1,BTNFLAGS      ; Called when we are suspended for any reason - WRIST_SUSPEND
        rts
L0116:  jmp     FLASH   ; Called to handle any timers or time events - WRIST_DOTIC
L0119:  bclr    1,BTNFLAGS      ; Called when the COMM app starts and we have timers pending - WRIST_INCOMM
        rts
L011c:  rts             ; Called when the COMM app loads new data - WRIST_NEWDATA
        nop
        nop

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

L0123:  jmp   HANDLE_STATE0
        db    STATETAB-STATETAB
;
; (3) Program strings
;
S6_MARQ timex6  "   +O+   "
S8_TITLE timex  " PICK-6 "

MARQ_SEL
        DB      S6_MARQ+2-START
        DB      S6_MARQ+3-START
        DB      S6_MARQ+2-START
        DB      S6_MARQ+1-START
        DB      S6_MARQ-START
        DB      S6_MARQ+1-START
;
; (4) State Table
;
STATETAB:
        db  	0
        db	EVT_ENTER,TIM2_16TIC,0 	; Initial state
        db      EVT_RESUME,TIM_ONCE,0   ; Resume from a nested app
        db  	EVT_DNNEXT,TIM2_16TIC,0	; Next button
        db      EVT_TIMER2,TIM_ONCE,0   ; Timer
        db  	EVT_MODE,TIM_ONCE,$FF	; Mode button
        db  	EVT_END

PICK_VALS       db      0,0,0,0,0,0,0,$FF
;
; (5) This flashes the text on the screen
;
FLASH
        lda     CURRENT_APP     ; See which app is currently running
        cmp     #APP_WRIST      ; Is it us?
        bne     L0113           ; No, so just turn off the tic timer since we don't need it
        ldx     #5
        lda     MARQ_POS
        jsr     INCA_WRAPX
        sta     MARQ_POS
        tax
        lda     MARQ_SEL,X
        jsr     PUT6MID
        ldx     MARQ_POS
        lda     MARQ_SEL,X
        jmp     PUT6TOP
;
; (6) They want us to do it again
;
DOITAGAIN	                ; Tell them we are going to do it again
        clr     MARQ_POS
        bset    1,BTNFLAGS
        jsr     CLEARALL
        jmp     BANNER
;
; (7) State Table 0 Handler
; This is called to process the state events.
; We see ENTER, RESUME, TIMER2 and NEXT events
;        
HANDLE_STATE0:
        bset    1,APP_FLAGS             ; Indicate that we can be suspended
        bclr    1,BTNFLAGS
        lda     BTNSTATE
        cmp     #EVT_DNNEXT             ; Did they press the next button?
        beq     DOITAGAIN
        cmp     #EVT_ENTER              ; Or did we start out
        beq     DOITAGAIN
        cmp     #EVT_RESUME             
	beq     REFRESH
;
; (8) Select a random answer
;
SHOWIT
        clra
        ldx     #6
CLEARIT 
        sta     PICK_VALS-1,X
        decx
        bne     CLEARIT
;
; We want to pick 6 random numbers.  The first needs to be in the range 1 ... RAND_RANGE
; The second should be in the range 1 ... (RAND_RANGE-1)
; The third should be in the range 1 ... (RAND_RANGE-2)
; The fourth should be in the range 1 ... (RAND_RANGE-3)
; The fifth should be in the range 1 ... (RAND_RANGE-4)
; The sixth should be in the range 1 ... (RAND_RANGE-5)
;
        clr     THIS_PICK
ONE_MORE_PICK

REPICK
        jsr     RAND16
        and     #63
        sta     TEMPL
        lda     #RAND_RANGE
        sub     THIS_PICK
        cmp     TEMPL
        blo     REPICK
        lda     TEMPL
        bsr     INSERT_NUM

        inc     THIS_PICK
        lda     THIS_PICK
        cmp     #6
        bne     ONE_MORE_PICK
        bra     REFRESH
;
; (9) Insert a number in the list
;
INSERT_NUM
        inca
        ldx     #(PICK_VALS-1)-BASE_TAB   ; Index so that we can use the short addressing mode
TRY_NEXT
        incx                            ; Advance to the next number
        tst     BASE_TAB,X              ; Is it an empty slot?
        bne     NOT_END                 ; No, try some more
        sta     BASE_TAB,X              ; Yes, just toss it in there
        rts                             ; And return
NOT_END
        cmp     BASE_TAB,X              ; Non-empty slot, are we less than it?
        blo     PUT_HERE                ; Yes, so we go here
        inca                            ; No, Greater than or equal, we need to increment one and try again
        bra     TRY_NEXT
PUT_HERE
        sta     TEMPL
        lda     BASE_TAB,X
        sta     TEMPH
        lda     TEMPL
        sta     BASE_TAB,X
        lda     TEMPH
        incx
        tsta
        bne     PUT_HERE
        rts
;
; (10) Display the currently selected random numbers
;
REFRESH
        ldx     PICK_VALS
        bsr     GOFMTX
        jsr     PUTTOP12

        ldx     PICK_VALS+1
        bsr     GOFMTX
        jsr     PUTTOP34

        ldx     PICK_VALS+2
        bsr     GOFMTX
        jsr     PUTTOP56

        ldx     PICK_VALS+3
        bsr     GOFMTX
        jsr     PUTMID12

        ldx     PICK_VALS+4
        bsr     GOFMTX
        jsr     PUTMID34

        ldx     PICK_VALS+5
        bsr     GOFMTX
        jsr     PUTMID56

        lda     #ROW_MP23
        sta     DISP_ROW
        bset    COL_MP23,DISP_COL

        lda     #ROW_MP45
        sta     DISP_ROW
        bset    COL_MP45,DISP_COL

        lda     #ROW_TP23
        sta     DISP_ROW
        bset    COL_TP23,DISP_COL

        lda     #ROW_TP45
        sta     DISP_ROW
        bset    COL_TP45,DISP_COL
BANNER
        lda	#S8_TITLE-START ; And show the mode on the bottom
        jmp	BANNER8

GOFMTX  JMP     FMTX
; (11) Here is an excellent random number generator
; it comes courtesy of Alan Beale <biljir@pobox.com%gt;
; The following C code gives a good MWC (multiply-with-carry)
; generator.  This type is generally superior to linear
; congruential generators.  As a bonus, there is no particular advantage to using the high-order
; rather than the low-order bits.
; The algorithm was developed and analyzed by George
; Marsaglia, a very well-known scholar of random number lore.
;
; The code assumes 16 bit shorts and 32 bit longs (hardly surprising).
;
;static unsigned short wn,wc;  /* random number and carry */
;
;unsigned short rand() {
;    unsigned long temp;
;    temp = 18000*wn + wc;
;    wc = temp >> 16;
;    wn = temp & 0xffff;
;    return wn;
;}
;
;To seed, set wn to anything you like, and wc to anything between 0 and 17999.
;
; Translating this into assembler is
;nHnL*0x4650 + RAND_WCHcL
;
;    unsigned long temp;
;    temp = 18000*wn + wc;
;    wc = temp >> 16;
;    wn = temp & 0xffff;
;    return wn;
;     temp = 0x4650 * n + c
;     temp = 0x4650 * nHnL + cHcL
;     temp = (0x4600 + 0x50) * (nH00 + nL) + cHcL
;     temp = 0x4600*nH00 + 0x4600*nL + 0x50*nH00 + 0x50*nL + cHcL
;     temp = 0x46*nH*0x10000 + 0x46*nL*0x100 + 0x50*nH*0x1000 + 0x50*nL + cHcL
; We construct the 32bit result into tH tL cH cL and then swap the 16 bit values
; once we have no more need of the original numbers in the calculation
;
RAND_MULT       EQU     18000   ; This is for the random number generator
RAND_MULTH      EQU     RAND_MULT/256
RAND_MULTL      EQU     RAND_MULT&255

RAND16
        lda     RAND_WNL        ; A=nL
        ldx     RAND_MULTL      ; X=0x50
        mul                     ; X:A = 0x50*nL
        add     RAND_WCL        ; A=Low(0x50nL)+cL
        sta     RAND_WCL        ; cL=Low(0x50nL)+cL
        txa                     ; A=High(0x50nL)
        adc     RAND_WCH        ; A=High(0x50nL)+cH
        sta     RAND_WCH        ; cH=High(0x50nL)+cH
        clra                    ; A=0
        sta     TEMPH           ; tH=0
        adc     #0              ; A=Carry(0x50nL)+cH
        sta     TEMPL           ; tL=Carry(0x50nL)+cH

        lda     RAND_WNL        ; A=nL
        ldx     RAND_MULTH      ; X=0x46
        bsr     RAND_SUB        ; tL:cH += 0x46*nL  tH=carry(0x46*nL)

        lda     RAND_WNH        ; A=nH
        ldx     RAND_MULTL      ; X=0x50
        bsr     RAND_SUB        ; tL:cH += 0x50*nH  tH=carry(0x50*nH)

        lda     RAND_WNH        ; A=nH
        ldx     RAND_WCL        ; X=cL
        stx     RAND_WNL        ; nL=cL
        ldx     RAND_WCH        ; X=cH
        stx     RAND_WNH        ; hH=cH
        ldx     RAND_MULTH      ; X=0x46
        mul                     ; X:A=0x46*nH
        add     TEMPL           ; A=Low(0x46*nH)+tL
        sta     RAND_WCL        ; nL=Low(0x46*nH)+tL
        txa                     ; A=High(0x46*nH)
        adc     TEMPH           ; A=High(0x46*nH)+tH
        sta     RAND_WCH        ; nH=High(0x46*nH)+tH
        rts

RAND_SUB
        mul                     ; Compute the values
        add     RAND_WCH        ; A=LOW(result)+cH
        sta     RAND_WCH        ; cH=Low(result)+cH
        txa                     ; X=High(result)
        adc     TEMPL           ; X=High(result)+tL+Carry(low(result)+cH)
        sta     TEMPL           ; tL=High(result)+tL+Carry(low(result)+cH)
        clra                    ; A=0
        adc     TEMPH           ; A=carry(High(result)+tL+Carry(low(result)+cH))+tH
        sta     TEMPH           ; tH=carry(High(result)+tL+Carry(low(result)+cH))+tH
        rts
;
; (12) 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
        lda     CURRENT_TIC
        sta     RAND_WNL
        sta     RAND_WNH
        sta     RAND_WCL
        and     #$3f
        sta     RAND_WCH
	rts
  1. Program specific constants - We have several variables - RAND_WCL, RAND_WCH, RAND_WNL and RAND_WNH which we use for the random number routine. CURRENT_TIC is what is set by the system when it reads the clock to keep the watch time up to date.  We use it once to provide a seed for the random number generator. Note that we are overlapping the use of THIS_PICK and MARQ_POS to save one byte of low ram.
  2. System entry point vectors - identical to the 3BALL example, This one gets to be a little fun.  Notice for the WRIST_SUSPEND and WRIST_INCOMM routines that we don't have a JMP instruction, but instead put the actual code in line.  This saves use a couple of bytes.
  3. Program strings - We are pretty fruegal here in reusing blanks at the end of the string very liberally.  Also note the S6_MARQ string which has blanks at the start and end so that it can shuffle left and right on the display but always have blanks visible.  The MARQ_SEL and MSG_SEL tables are simply offsets that allow us to select the message with a simple load instruction instead of having to caluclate the offset.
  4. State Table - This is pretty vanilla here except for the fact that we have a very long time interval after the DNNEXT and ENTER events.  It is during this time that the Marquis runs.  We could make it even longer, but this seems to be a good compromise between seeing something happen and actually getting a result in a resonable time.
  5. State Table 0 Handler - Extremely simple, there are only four events that we want to see and this is the typical test and branch one.  THe only unique thing here is that we turn off the Marquis timer as soon as we get any event.
  6. This flashes the text on the screen - This is the cheap way to do a Marquis.  Just have a string wider than the display and change the offset from the start at which you start to display.  For this one, there are only 6 states and we select the starting offset from the table based on our current cycle.  Note that this routine is called by the TIC timer which is enabled when they want a new random number.  Eventually the timer for the main event will run out and they will simply stop calling us.
  7. They want us to do it again - Whenever we want to do a new random number, we just start the Marquis tic timer and set up the display.
  8. Select a random answer - This is really the meat of this wristapp. We need to pick 6 random numbers and sort them. Fortunately, we can take advantage of the sorting as part of our random number selection.
  9. Insert a number in the list - Given a random number, add it to the list of random numbers in sorted order. Essentially, we start at the begining of the list and go until we either find a slot where we need to insert the number in order or we hit the end of the list. If we hit the end of the list, we store the number there and return. Otherwise we insert the number at the appropriate spot. One additional thing that we do is increment the number by 1 for each entry in the that is less than it. It makes sense, but you need to think about why this works.
  10. Display the currently selected random numbers - Given a the 6 random numbers, we just put them on the display separated by periods. Note the series of BSR unstructions to the GOFMTX label. Since there were 6 calls to it, we were about to reduce the 6 3-byte instructions to 6 2-byte instructions plus one 3-byte instruction to do the call for a savings of 3 bytes.
  11. Here is a random number generator - This is great random number generator that you might want to grab for any other code that you might write.
  12. This is the main initialization routine which is called when we first get the app into memory - Very boring stuff here, but we do take a moment to initialize the random number seed with the current tic count just to make it a little more variable.
1