; ; rand.s ; ; Generating random numbers ; ; © 1994-1998 Straylight ; ;----- Licensing note ------------------------------------------------------- ; ; This file is part of Straylight's Sapphire library. ; ; Sapphire is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2, or (at your option) ; any later version. ; ; Sapphire is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with Sapphire. If not, write to the Free Software Foundation, ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;----- Standard header ------------------------------------------------------ GET libs:header GET libs:swis ;----- External dependencies ------------------------------------------------ GET sapphire:divide GET sapphire:sapphire ;----- Main code ------------------------------------------------------------ AREA |Sapphire$$Code|,CODE,READONLY ; --- rand --- ; ; On entry: -- ; ; On exit: R0 == a pseudorandom number between 0 and &7FFFFFFF ; ; Use: Returns a pseudorandom number. The algorithm used is the ; additive generator found in Knuth. The table is generated ; from the current monotonic time using a linear congruential ; generator. Randomness is fairly good, and it's very quick. EXPORT rand rand ROUT STMFD R13!,{R1-R3,R12,R14} ;Save some registers WSPACE rand__wSpace ;Find my workspace address LDMIB R12,{R1,R2} ;Load the table indices ADR R3,rand__table ;Find the ring buffer LDR R0,[R3,R1,LSL #2] ;Load the value x[n-24] LDR R14,[R3,R2,LSL #2] ;Load the value x[n-55] ADD R0,R0,R14 ;And form the new number STR R0,[R3,R2,LSL #2] ;Store new number in buffer SUBS R1,R1,#1 ;Decrement x-24 index MOVLT R1,#54 ;If too small, wrap it round SUBS R2,R2,#1 ;Decrement x-55 index MOVLT R2,#54 ;If too small, wrap it round STMIB R12,{R1,R2} ;Save the indices back again BIC R0,R0,#&80000000 ;Clear the top bit (positive) LDMFD R13!,{R1-R3,R12,PC}^ ;Return to caller LTORG ; --- rand_setSeed --- ; ; On entry: R0 == a pseudorandom seed ; ; On exit: -- ; ; Use: Sets up the random number generator (rand) to the given start ; position. The table of values is initialised from the seed ; in a psuedorandom manner, using a linear congruential ; generator. EXPORT rand_setSeed rand_setSeed ROUT STMFD R13!,{R0-R2,R12,R14} ;Save some registers WSPACE rand__wSpace ;Find my workspace address ; --- Reset the indices --- MOV R1,#23 ;Start 24-index at 24-1 MOV R2,#54 ;Start 55-index at 55-1 STMIB R12,{R1,R2} ;Save them away ; --- Now start populating the table --- ADR R1,rand__table ;Point to the ring buffer MOV R2,#55 ;Counter for the items 00rand_setSeed ADD R14,R0,R0,LSR #16 ;Mangle the seed a little STR R14,[R1],#4 ;Save it in the table ADD R14,R0,R0,LSL #2 ;Do multiply op in generator RSB R14,R14,R14,LSL #4 RSB R0,R0,R14,LSL #2 RSB R0,R0,R0,LSL #3 ADD R0,R0,R0,LSL #5 ADD R0,R0,#&66000000 ;Do add op in generator ADD R0,R0,#&00D60000 ADD R0,R0,#&00001900 ADD R0,R0,#&000000E1 SUBS R2,R2,#1 ;Decrement the counter BGT %00rand_setSeed ;If not finished, go again LDMFD R13!,{R0-R2,R12,PC}^ ;Return to caller LTORG ; --- rnd --- ; ; On entry: R0 == start value (inclusive) ; R1 == end value (inclusive) ; ; On exit: R0 == random number between the boundaries given ; ; Use: Returns a random integer between the boundaries given. ; The distribution is slightly skewed towards lower numbers, ; but there's not a lot I can do about this, folks. EXPORT rnd rnd ROUT STMFD R13!,{R1,R2,R14} ;Save a bunch of registers MOV R2,R0 ;Look after the start value SUB R1,R1,R0 ;Subtract the initial value ADD R1,R1,#1 ;Make it inclusive nicely BL rand ;Get a random number BL divide ;Force it to be in range ADD R0,R1,R2 ;And add the start value LDMFD R13!,{R1,R2,PC}^ ;Return to caller LTORG ; --- rand_init --- ; ; On entry: -- ; ; On exit: -- ; ; Use: Initialise the random number table. EXPORT rand_init rand_init ROUT STMFD R13!,{R12,R14} ;Save a register or two WSPACE rand__wSpace ;Find my workspace LDR R14,rand__flags ;Load my flags word TST R14,#rFlag__inited ;Am I initialised yet? LDMNEFD R13!,{R12,PC}^ ;Yes -- don't do it again ORR R14,R14,#rFlag__inited ;Set the flag then STR R14,rand__flags ;And save back the new flags STMFD R13!,{R0} ;Save another register SWI OS_ReadMonotonicTime ;Load the current time BL rand_setSeed ;Set up the random table LDMFD R13!,{R0,R12,PC}^ ;And return to caller LTORG rand__wSpace DCD 0 ;------ Workspace ----------------------------------------------------------- ^ 0,R12 rand__wStart # 0 rand__flags # 4 ;Various flaglike objects rand__24 # 4 ;Offset to item 24 in buffer rand__55 # 4 ;Offset to item 55 in buffer rand__table # 55*4 ;The random number table rand__wSize EQU {VAR}-rand__wStart rFlag__inited EQU (1<<0) ;Am I initialised yet? AREA |Sapphire$$LibData|,CODE,READONLY DCD rand__wSize DCD rand__wSpace DCD 0 DCD rand_init ;----- That's all, folks ---------------------------------------------------- END