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.