chiark / gitweb /
JPEG support and other fixes from Nick Clark
[ssr] / StraySrc / Libraries / Sapphire / s / rand
1 ;
2 ; rand.s
3 ;
4 ; Generating random numbers
5 ;
6 ; © 1994-1998 Straylight
7 ;
8
9 ;----- Licensing note -------------------------------------------------------
10 ;
11 ; This file is part of Straylight's Sapphire library.
12 ;
13 ; Sapphire is free software; you can redistribute it and/or modify
14 ; it under the terms of the GNU General Public License as published by
15 ; the Free Software Foundation; either version 2, or (at your option)
16 ; any later version.
17 ;
18 ; Sapphire is distributed in the hope that it will be useful,
19 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 ; GNU General Public License for more details.
22 ;
23 ; You should have received a copy of the GNU General Public License
24 ; along with Sapphire.  If not, write to the Free Software Foundation,
25 ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27 ;----- Standard header ------------------------------------------------------
28
29                 GET     libs:header
30                 GET     libs:swis
31
32 ;----- External dependencies ------------------------------------------------
33
34                 GET     sapphire:divide
35                 GET     sapphire:sapphire
36
37 ;----- Main code ------------------------------------------------------------
38
39                 AREA    |Sapphire$$Code|,CODE,READONLY
40
41 ; --- rand ---
42 ;
43 ; On entry:     --
44 ;
45 ; On exit:      R0 == a pseudorandom number between 0 and &7FFFFFFF
46 ;
47 ; Use:          Returns a pseudorandom number.  The algorithm used is the
48 ;               additive generator found in Knuth.  The table is generated
49 ;               from the current monotonic time using a linear congruential
50 ;               generator.  Randomness is fairly good, and it's very quick.
51
52                 EXPORT  rand
53 rand            ROUT
54
55                 STMFD   R13!,{R1-R3,R12,R14}    ;Save some registers
56                 WSPACE  rand__wSpace            ;Find my workspace address
57                 LDMIB   R12,{R1,R2}             ;Load the table indices
58                 ADR     R3,rand__table          ;Find the ring buffer
59                 LDR     R0,[R3,R1,LSL #2]       ;Load the value x[n-24]
60                 LDR     R14,[R3,R2,LSL #2]      ;Load the value x[n-55]
61                 ADD     R0,R0,R14               ;And form the new number
62                 STR     R0,[R3,R2,LSL #2]       ;Store new number in buffer
63                 SUBS    R1,R1,#1                ;Decrement x-24 index
64                 MOVLT   R1,#54                  ;If too small, wrap it round
65                 SUBS    R2,R2,#1                ;Decrement x-55 index
66                 MOVLT   R2,#54                  ;If too small, wrap it round
67                 STMIB   R12,{R1,R2}             ;Save the indices back again
68                 BIC     R0,R0,#&80000000        ;Clear the top bit (positive)
69                 LDMFD   R13!,{R1-R3,R12,PC}^    ;Return to caller
70
71                 LTORG
72
73 ; --- rand_setSeed ---
74 ;
75 ; On entry:     R0 == a pseudorandom seed
76 ;
77 ; On exit:      --
78 ;
79 ; Use:          Sets up the random number generator (rand) to the given start
80 ;               position.  The table of values is initialised from the seed
81 ;               in a psuedorandom manner, using a linear congruential
82 ;               generator.
83
84                 EXPORT  rand_setSeed
85 rand_setSeed    ROUT
86
87                 STMFD   R13!,{R0-R2,R12,R14}    ;Save some registers
88                 WSPACE  rand__wSpace            ;Find my workspace address
89
90                 ; --- Reset the indices ---
91
92                 MOV     R1,#23                  ;Start 24-index at 24-1
93                 MOV     R2,#54                  ;Start 55-index at 55-1
94                 STMIB   R12,{R1,R2}             ;Save them away
95
96                 ; --- Now start populating the table ---
97
98                 ADR     R1,rand__table          ;Point to the ring buffer
99                 MOV     R2,#55                  ;Counter for the items
100
101 00rand_setSeed  ADD     R14,R0,R0,LSR #16       ;Mangle the seed a little
102                 STR     R14,[R1],#4             ;Save it in the table
103
104                 ADD     R14,R0,R0,LSL #2        ;Do multiply op in generator
105                 RSB     R14,R14,R14,LSL #4
106                 RSB     R0,R0,R14,LSL #2
107                 RSB     R0,R0,R0,LSL #3
108                 ADD     R0,R0,R0,LSL #5
109
110                 ADD     R0,R0,#&66000000        ;Do add op in generator
111                 ADD     R0,R0,#&00D60000
112                 ADD     R0,R0,#&00001900
113                 ADD     R0,R0,#&000000E1
114
115                 SUBS    R2,R2,#1                ;Decrement the counter
116                 BGT     %00rand_setSeed         ;If not finished, go again
117
118                 LDMFD   R13!,{R0-R2,R12,PC}^    ;Return to caller
119
120                 LTORG
121
122 ; --- rnd ---
123 ;
124 ; On entry:     R0 == start value (inclusive)
125 ;               R1 == end value (inclusive)
126 ;
127 ; On exit:      R0 == random number between the boundaries given
128 ;
129 ; Use:          Returns a random integer between the boundaries given.
130 ;               The distribution is slightly skewed towards lower numbers,
131 ;               but there's not a lot I can do about this, folks.
132
133                 EXPORT  rnd
134 rnd             ROUT
135
136                 STMFD   R13!,{R1,R2,R14}        ;Save a bunch of registers
137                 MOV     R2,R0                   ;Look after the start value
138                 SUB     R1,R1,R0                ;Subtract the initial value
139                 ADD     R1,R1,#1                ;Make it inclusive nicely
140                 BL      rand                    ;Get a random number
141                 BL      divide                  ;Force it to be in range
142                 ADD     R0,R1,R2                ;And add the start value
143                 LDMFD   R13!,{R1,R2,PC}^        ;Return to caller
144
145                 LTORG
146
147 ; --- rand_init ---
148 ;
149 ; On entry:     --
150 ;
151 ; On exit:      --
152 ;
153 ; Use:          Initialise the random number table.
154
155                 EXPORT  rand_init
156 rand_init       ROUT
157
158                 STMFD   R13!,{R12,R14}          ;Save a register or two
159                 WSPACE  rand__wSpace            ;Find my workspace
160                 LDR     R14,rand__flags         ;Load my flags word
161                 TST     R14,#rFlag__inited      ;Am I initialised yet?
162                 LDMNEFD R13!,{R12,PC}^          ;Yes -- don't do it again
163
164                 ORR     R14,R14,#rFlag__inited  ;Set the flag then
165                 STR     R14,rand__flags         ;And save back the new flags
166                 STMFD   R13!,{R0}               ;Save another register
167                 SWI     OS_ReadMonotonicTime    ;Load the current time
168                 BL      rand_setSeed            ;Set up the random table
169                 LDMFD   R13!,{R0,R12,PC}^       ;And return to caller
170
171                 LTORG
172
173 rand__wSpace    DCD     0
174
175 ;------ Workspace -----------------------------------------------------------
176
177                 ^       0,R12
178 rand__wStart    #       0
179
180 rand__flags     #       4                       ;Various flaglike objects
181
182 rand__24        #       4                       ;Offset to item 24 in buffer
183 rand__55        #       4                       ;Offset to item 55 in buffer
184
185 rand__table     #       55*4                    ;The random number table
186
187 rand__wSize     EQU     {VAR}-rand__wStart
188
189 rFlag__inited   EQU     (1<<0)                  ;Am I initialised yet?
190
191                 AREA    |Sapphire$$LibData|,CODE,READONLY
192
193                 DCD     rand__wSize
194                 DCD     rand__wSpace
195                 DCD     0
196                 DCD     rand_init
197
198 ;----- That's all, folks ----------------------------------------------------
199
200                 END