; LOTTO.MAC ; ;************************************************* ;** ** ;** A RANDOM NUMBER GENERATOR TO SIMULATE ** ;** LOTTERY DRAWINGS, ETC. ** ;** ** ;** by ** ;** John Fox ** ;** (c) l986 ** ;************************************************* ; ; THIS PROGRAM MAKES USE OF SYSLIB ; FUNCTIONS AND A RANDOM NUMBER ; GENERATOR THAT USES THE Z-80 REFRESH ; REGISTER. ; .Z80 ;IT IS IN Z80 CODE ; ;-------------------------------- ;EXTERNAL REFERENCES FOR SYSLIB ;-------------------------------- ; EXT MULHD ;16 bit multiply EXT PRINT ;console display routine EXT MA3DC ;routine to store acc. as 3 dec. digits EXT CIN ;routine to get chr from console EXT COUT ;routine to output chr to console EXT PADC ;routine to output acc. as dec. # ; ;-------------------------------- ; ABSOLUTE EQUATES ;-------------------------------- ; BDOS EQU 05H ;BDOS base-page entry point FCB EQU 5CH CR EQU 0DH LF EQU 0AH BS EQU 08H ; ; ;-------------------------------------------------------- ; START OF PROGRAM ;-------------------------------------------------------- LOTTO: JP LOTTO1 ;jump over the following subroutine ;--------------- ; FIRST A LITTLE ROUTINE TO CLEAR THE SCREEN ; It is used throughout the entire program ; CLRSTG: DB 1AH,00,00,00,00,00 ;put here whatever will clear the screen CLSCRN: PUSH HL ;save registers PUSH PSW LD HL,CLRSTG ;point to DB's MRSTRG: LD A,(HL) ;get one CP 00 ;is it zero JR Z,CLRDN ;then we're done CALL COUT ;otherwise display it INC HL ;and point to next one JR MRSTRG ;and go back to display it ; CLRDN: POP PSW ;restore registers POP HL RET ;and exit ;--------------- ; ; NOW THE REAL PROGRAM ; LOTTO1: CALL CLSCRN ;clear the screen LD HL,STORE ;make sure storage counter is reset LD A,00 LD (HL),A CALL PRINT ;call syslib print routine ;which prints following dbs DB ' LOTTO ',lf,lf,cr DB ' A Random Number Program',LF,CR DB ' To Simulate Lottery Drawings, etc.',lf,lf,cr DB ' (c) J. Fox' DB CR,LF,LF,LF,LF DB ' How many numbers do you want chosen?',CR,LF DB ' (Please, no more than 12)....... ',00 LD HL,HOW$MANY ;point to current value LD A,(HL) ;get it CALL PADC ;display it CALL PRINT ;erase trailing #'s if any DB BS,BS,00 CALL INSTRG ;get string input from console LD DE,HOW$MANY CALL FIXINP ;put new numbers into storage CALL PRINT DB CR,LF,LF,LF,LF DB ' What is the greatest value any one can have?',CR,LF DB ' (Please, no greater than 99)......... ',BS,BS,00 LD HL,MAXVAL ;point to current value LD A,(HL) ;get it CALL PADC ;display it CALL PRINT ;clean up trailing #'s DB BS,BS,00 CALL INSTRG ;get new #'s LD DE,MAXVAL CALL FIXINP TYPE: CALL PRINT DB CR,LF,LF,LF,LF DB ' Hit for LOTTO (unique #''s)',CR,LF DB ' Hit for non-unique #''s (including 0)' DB CR,LF,00 LD B,0FFH ;load B with flag value for LOTTO-type selection CALL CIN ;get input CP CR ;is it LOTTO? JR Z,LDIT ;if so, go load flag value CP 1BH ;if not, was it an ESC? JP NZ,TYPE ;if not, illegal input, wait for CR or ESC INC B ;otherwise adjust flag value LDIT: LD HL,FLAG ;point to flag storage location LD A,B ;get flag value LD (HL),A ;store it ; SETUP: LD HL,HOW$MANY ;point to # of selections requested LD A,(HL) ;get it CP 00H ;is it a positive # JP NZ,NXTCK ;if so check some more CALL PRINT ;if not, comment DB CR,LF,LF,' Ahah! Let me get this right. You want me to choose no numbers from among a' DB CR,LF,' bunch of numbers? That''s more like a Zen exercise.' DB CR,LF,' Now the way I see it I could either go off to a circuit monastery for a few hours' DB CR,LF,' to meditate on the task or we could start over fresh.' DB CR,LF,' Let''s do that.....' DB CR,LF,LF,LF,' Hit any key.',00 CALL CIN JP LOTTO NXTCK: CP 0DH ;is it greater than 12? JP C,HMOK ;if not, OK CALL PRINT ;otherwise, complain DB CR,LF,LF,' Hold on now.....' DB CR,LF,' I thought I said I couldn''t deal with more than twelve selections.' DB CR,LF,' Now we''ll have to start all over again........' DB CR,LF,LF,LF,' Hit any key.',00 CALL CIN ;wait for input JP LOTTO HMOK: LD B,A ;set it aside DEC HL ;point to MAXVAL LD A,(HL) ;get largest number allowed PUSH PSW ;save it LD HL,MVOK1 ;set up return address if non-unique # drawing PUSH HL LD HL,LKJ ;and return address if unique # drawing JP WHTFLG ;and go check LOTTO flag ; ; LKJ: POP PSW ;get max value back in A for unique drawings CP B ;is it smaller than # of selections JP NC,MVOK ;if not, fine CALL PRINT ;otherwise complain DB CR,LF,LF DB ' This must be some kind of a trick, right?' DB CR,LF,' You want me to choose',00 LD C,A ;put MAXVAL aside for a moment LD A,B ;and get HOW$MANY CALL PADC ;print HOW$MANY on screen CALL PRINT DB ' numbers from among',00 LD A,C ;then display MAXVAL CALL PADC CALL PRINT DB ' numbers?' DB CR,LF,' I''d love to be able to do that, but I''m not a magician.' DB CR,LF,' Let''s go back and start over....' DB CR,LF,LF,LF,' Hit any key.',00 CALL CIN ;wait for input JP LOTTO MVOK1: POP PSW ;if we're coming from flag check get MAXVAL back MVOK: LD HL,MASK ;Point to storage space for max mask LD B,A ;put MAXVAL into B LD A,0FH ;load up with smallest possible mask DIFF: LD D,A ;put it in a safe place SUB B ;is it big enough? JR NC,SAV ;it is big enough, use it LD A,D ;otherwise get mask back RLA ;double it plus one JP DIFF ;and loop back to see if it is big enough SAV: LD BC,VALDIFF LD (BC),A ;store value difference LD (HL),D ;and store mask ; BEGIN: CALL CLSCRN CALL PRINT DB CR,LF,LF,LF,LF,LF,LF,LF DB ' Give me just a half second while I try to concentrate.',cr,lf DB ' Then hit the key marked "esc".',cr,lf,lf DB ' I will tell you what numbers I can see.',cr,lf,lf DB ' Here goes...................',cr,lf,lf,00 ; START: CALL CIN ;wait for go-ahead CP 1BH JR NZ,START RNDLP: CALL RANFUN ;get first 16-bit random number on stack POP DE ;pop it into DE register LD HL,HOW$MANY ;point to # of selections needed LD E,(HL) ;get # LD HL,MASK ;point to mask LD B,(HL) ;put it in B LD A,D ;get half of random number in accumulator AND B ;mask to eliminate unneeded binary digits LD HL,VALDIFF LD B,(HL) SUB B ;subtract to shift #s down from max of 63 decimal ;to max of 44 decimal JR C,RNDLP ;if result is negative get new random # LD HL,RNDLP ;if not point to beginning of routine and LD D,A ;save number just in case CALL Z,WHTFLG ;if result is zero go see if legal or not NMSTR: LD HL,STORE ;point to storage for resultant numbers LD A,(HL) ;get # of times we have already stored #'s CP E ;compare to max # allowed EXIT: JP Z,DONE ;if max, we are done INC HL ;move over one to first storage position LD C,A ;put counter value in C LD B,00 ;zero B CP 00 ;if # of chrs stored is zero LD A,D ;get rnd # back JR Z,STORIT ;and start storing in first position PUSH BC ;save BC counter PUSH HL ;save pointer to buffer LD HL,UNIQ ;point to uniqueness test CALL WHTFLG ;and go see if we need it POP HL ;if not restore buffer pointer POP BC ;restore counter ADD HL,BC ;adjust pointer to first free position LD A,D ;get random # back JR STORIT ;and go store it UNIQ: POP HL ;here's where we come back if uniqueness needed POP BC LD A,D ;get random # back CPIR ;and compare to #'s stored already JR Z,RNDLP ;if it is duplicate get another one ;we should now be pointing to first empty ;position so... STORIT: LD (HL),A ;put rnd # into memory LD HL,STORE ;point to memory counter LD A,(HL) ;get it INC A ;update it LD (HL),A ;put it back JR RNDLP ;and go back for next random # ;---------------- ; A SMALL ROUTINE TO ALTER FLOW IF LOTTO FLAG IS SET ; (accumulator is affected, HL contains return address for unique ; drawings, stack contains it for non-unique.) ; WHTFLG: PUSH HL ;save pointer LD HL,FLAG ;point to LOTTO flag LD A,(HL) ;get it POP HL ;restore pointer RLC A ;shift flag over one place RET NC ;continue if zero & duplicates allowed INC SP ;otherwise lose RET address INC SP JP (HL) ;and jump to predetermined location ; ;----------------------------------------------------------------- ;END PRINT OUT ROUTINE ;----------------------------------------------------------------- ; ;This routine takes the binary numbers stored in the buffer and ;prints them out to the screen as decimal #s in the following ;format: ; ## ## ## ## ## ## (ETC.) ; ;Three subroutines follow, then the control program. ; ;--------------- ; THIS ONE CALCULATES SPACES NEEDED TO MATCH #'S ; AND OUTPUTS THE ACCUMULATOR TO THE SCREEN TO FILL ; FILLIN: PUSH PSW ;save fill character LD HL,HOW$MANY ;point to number of selections LD A,(HL) ;get it and multiply # by 4 RLA ;to get space we need to fill RLA LD B,A POP PSW ;get fill character NXT: CALL COUT ;output it DEC B JR NZ,NXT RET ;--------------- ; THIS TYPES A LINE OF ASTERISKS ; ASTXLN: LD A,2AH CALL FILLIN CALL PRINT DB '****************************' DB CR,LF,00 RET ;--------------- ; THIS TYPES A LINE OF SPACES WITH AN ASTERISK AT BOTH ENDS ; BLNKLN: CALL PRINT DB '* ',00 LD A,20H CALL FILLIN CALL PRINT DB '*',CR,LF,00 RET ;--------------- ; HERE IS THE CONTROL ROUTINE ; DONE: CALL CLSCRN CALL PRINT ;print following message DB LF,LF DB ' These are the numbers in my circuits right now...',cr,lf,lf,lf DB lf,lf,lf,lf,00 CALL ASTXLN CALL BLNKLN CALL PRINT DB '* ',00 LD HL,HOW$MANY ;point to total number of selections LD B,(HL) ;put total number in B LD HL,STORE+1 ;point to first number GO: LD A,20h ;output a space to console CALL COUT LD A,(HL) CALL PADC ;output number to console as decimal INC HL ;point to next number DEC B ;another down JR NZ,GO ;anymore to go? CALL PRINT DB ' *',CR,LF,00 CALL BLNKLN CALL ASTXLN LD A,2AH ; ;Now we have finished our output. ;We need only give someone the choice of repeating the whole procedure ; RPT: CALL PRINT DB CR,LF,LF,LF,LF,LF,LF,LF DB ' If you want another set, hit .',cr,lf DB ' Hit to terminate this program.',CR DB LF,00 WHT: CALL CIN ;get character entered at console CP 0DH ;is it a CR? JP Z,stop ;if so, we are done CP 1BH ;is it ESC? JP NZ,WHT ;if not keeping waiting CALL CLSCRN ;if so clear the screen LD HL,STORE ;point to storage counter LD (HL),00 ;zero it JP BEGIN ;and start over ; STOP: JP 00 ;GOODBYE ; ;------------------------------------ ; SUBROUTINES HANDLING CONSOLE INPUT ;------------------------------------ ; ; GET CONSOLE INPUT AS 2-CHARACTER STRING FOLLOWED BY ; IN CONSOLE BUFFER. ALLOW ONLY ASCII #'S ; INSTRG: LD A,00 LD HL,CNBUFF ;point to buffer LD (HL),A ;zero buffer counter GETCHR: CALL CIN ;get input CP CR ;if carriage return, we're done RET Z CP BS ;is it backspace? LD D,A ;save it anyway LD HL,CNBUFF ;point to buffer counter JR NZ,CHKCHR ;and go on to further check if not BS LD A,(HL) ;if BS get counter CP 00 JR Z,GETCHR ;if empty ignore backspace DEC A ;otherwise decrement counter LD (HL),A LD A,D ;get character back CALL COUT ;and output it to screen JP GETCHR ;and go back for input ; CHKCHR: CP 30H ;check that we have a number JR C,GETCHR ;no, too small CP 40H JR NC,GETCHR ;no, too big LD A,(HL) ;yes, get counter CP 2 JR Z,GETCHR ;if already full keep waiting for CR or BS INC A ;otherwise increment counter LD (HL),A ADD A,L ;adjust the pointer LD L,A LD A,D ;get character back LD (HL),A ;put it in buffer CALL COUT ;and display it CALL PRINT ;and clean up trailing space DB ' ',BS,00 ;just in case JR GETCHR ;and go back for more ; ; TAKE ASCII STRING IN CONSOLE BUFFER AND REDUCE TO A SINGLE ; NUMBER AND STORE IT AT LOCATION POINTED TO BY REGISTERS DE ; FIXINP: LD HL,CNBUFF ;point to buffer counter LD A,(HL) ;get it CP 00 ;anything entered? RET Z ;no, go with default value LD C,A ;yes, save buffer counter INC HL LD A,(HL) ;and get first character SUB 30H ;normalize it LD B,A ;set it aside LD A,C ;get counter CP 01 ;was this the only character? LD A,B JR Z,FIXDN ;yes, then we're done RLA ;if not multiply by 10 RLA RLA ADD A,B ADD A,B LD B,A ;set it aside INC HL LD A,(HL) ;get next number SUB 30H ;normalize it ADD A,B ;add them FIXDN: LD H,D ;get back original pointer LD L,E LD (HL),A ;and save result there RET ; ; ;------------------------------- ; RANDOM NUMBER GENERATOR ;------------------------------- ; ; Based on a routine by Robert Zimmerer in ; Microsystems, October '83. ; ; Returns a random 16 bit number on top of ; stack. Using the Z80 refresh register R for a ; random number seed. ; This routine calls an external routine ; MULHD that multiplies the HL and DE ; registers. ; RANFUN: LD A,R ; get a random byte LD E,A LD D,0 ; make a 16 bit number RRCA ; scramble and set carry ; randomly JR C,RAN1 ; use random number A if ; carry = 1 LD E,A ; else use scrambled value LD BC,RANB ; pint at random number B JR RAN2 RAN1: LD BC,RANA ; point at random number A RAN2: LD A,(BC) ; move previous random ; number into HL LD L,A INC BC LD A,(BC) LD H,A ADD HL,DE ; mix it up CALL MULHD ; scramble bits again ; HL = (HL+DE)*DE, BC=BC LD A,H ; and replace the previous ; random number with this new one LD (BC),A DEC BC LD A,L LD (BC),A EX (SP),HL ; swap random number with RET PUSH HL ; push back RET address RET ; return to calling routine ;with random number on stack RANB: DEFW 1936H ; initial random numbers RANA: DEFW 1936H ; just to get started RET ;**************************************************** ;* STORAGE SPACE ;**************************************************** CNBUFF: DS 05 ;console buffer for input STORE: DB 00 ;storage counter DS 12h ;storage space MAXVAL: DB 31H ;default highest number HOW$MANY: DB 06H ;default number of selections MASK: DB 00 VALDIFF: DB 00 FLAG: DB 0FFH ;FF= unique/non-zero selections ;00= non-unique with 00 legal selection END LOTTO _______________________________________________________________