aboutsummaryrefslogtreecommitdiff
path: root/rand.s
blob: f1b2c7201ff21ee522fd6b77c6994f68de3ce351 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

 .export _randl, _rand1to3
; .export _randb, _randi, _randl
; .export _rand1in5
; .export _randbit
 .importzp sreg, tmp3

 .include "atari.inc"

 .ifdef CART_TARGET
  .segment "HIGHCODE"
 .else
  .code
 .endif

; unsigned long __fastcall__ randl(void);
; this returns the full range of an unsigned long, 0 to 2**32-1
_randl:
 jsr _rand
 sta sreg
 jsr _rand
 sta sreg+1
 jsr _rand
 sta tmp3
 jsr _rand
 ldx tmp3
 rts

; return 1, 2, or 3. equivalent to: randi()%3+1
; replacing both occurences of the expression in taipan.c with a calls
; to this function saves 11 bytes.
_rand1to3:
	jsr _rand      ; returns 16 bits: X is MSB (which we ignore), A is LSB
	and #$03       ; A now 0..3
	beq _rand1to3  ; try again, if it's 0
	ldx #0         ; now A is 1..3, but we have to force X to 0...
	rts

;;; This rand() function copied from cc65-2.19's libsrc/common/rand.s
;;; and modified for my nefarious purposes.
;;; srand() is not present (we don't use it).
;
; Random number generator
;
; Written and donated by Sidney Cadot - sidney@ch.twi.tudelft.nl
; 2016-11-07, modified by Brad Smith
; 2019-10-07, modified by Lewis "LRFLEW" Fox
;
; May be distributed with the cc65 runtime using the same license.
;
;
; int rand (void);
; void srand (unsigned seed);
;
;  Uses 4-byte state.
;  Multiplier must be 1 (mod 4)
;  Added value must be 1 (mod 2)
;  This guarantees max. period (2**32)
;  The lowest bits have poor entropy and
;  exhibit easily detectable patterns, so
;  only the upper bits 16-22 and 24-31 of the
;  4-byte state are returned.
;
;  The best 8 bits, 24-31 are returned in the
;  low byte A to provide the best entropy in the
;  most commonly used part of the return value.
;
;  Uses the following LCG values for ax + c (mod m)
;  a = $01010101
;  c = $B3B3B3B3
;  m = $100000000 (32-bit truncation)
;
;  The multiplier was carefully chosen such that it can
;  be computed with 3 adc instructions, and the increment
;  was chosen to have the same value in each byte to allow
;  the addition to be performed in conjunction with the
;  multiplication, adding only 1 additional adc instruction.
;

 .export _rand, _randseed, _randseedl, _randseedh, _initrand, _addrandbits

.bss

; The seed. Not ANSI C compliant: we default to 0 rather than 1.
_randseedl:
_randseed:   .res 4
_randseedh = _randseed+2

.code

_rand:  clc
        lda     _randseed+0
        adc     #$B3
        sta     _randseed+0
        adc     _randseed+1
        sta     _randseed+1
        adc     _randseed+2
        sta     _randseed+2
        and     #$7f            ; Suppress sign bit (make it positive)
        tax
        lda     _randseed+2
        adc     _randseed+3
        sta     _randseed+3
        rts                     ; return bit (16-22,24-31) in (X,A)

; Initially the seed comes from sequential reads of POKEY's random
; register. It never returns 0 so we're guaranteed to have a usable
; seed.
_initrand:
 ldx #3
@l:
 lda RANDOM
 sta _randseed,x
 dex
 bpl @l
 rts

; Caller passes us a user keystroke as ATASCII, in A. We take bits 0
; to 2, wait that many scanlines, get a random number from POKEY, and
; EOR it into the _randseed byte pointed to by the low 2 bits of the
; frame counter (RTCLOK+2).
; Note that agetc() is still calling rand() on odd frames while all
; this is going on.
_addrandbits:
 and #$07
 tax
@l1:
 sta WSYNC
 dex
 bpl @l1
 lda RTCLOK+2
 and #$03
 tax
 lda RANDOM
@e:
 eor _randseed,x
 beq @e ; if the result is 0, undo the eor.
 sta _randseed,x
 rts


 ;;; rest of file is commented out

; RANDOM is the POKEY LFSR read address. According to the POKEY data
; sheet, this is the high 8 bits bits of a 17-bit LFSR (Atari calls it
; a poly counter). Unfortunately, a read from this address never seems
; to return 0, which confuses me: an LFSR can never return 0, but since
; we're only reading 8 bits of it, we should be able to get a 0 (some
; of the other 9 bits would still be 1).

; After some crude statistical analysis, I've decided to go with cc65's
; rand() implementation. It seems to return more evenly distributed
; results.

; Might use this at some point:
;_randbit:
; lda RANDOM
; asl
; lda #0
; adc #0
; rts

; unsigned char __fastcall__ randbit(void);
;_randbit:
; ldx #0
;randbit:
; lda RANDOM
; lsr
; and #$01
; rts

; This doesn't give evenly distributed results, it's twice as
; likely to return 2 or 3 than 0, 1, or 4.
; unsigned char __fastcall__ rand1in5(void);
;_rand1in5:
; ldx #0
;rand1in5:
; lda RANDOM
; lsr
; lsr
; and #$03
; adc #0
; rts


; unsigned char __fastcall__ randb(void);
;;_randb: ; C-callable entry point
;; ldx #0
;;randb:  ; asm-callable (doesn't trash X reg)
;; lda RANDOM ; bit 7 of this read ends up as bit 0 of result
;; sta tmp3
;; nop        ; let the LFSR cook for a bit...
;; nop
;; lda RTCLOK+2 ; different amount of cooking depending on whether
;; and #$01     ; we're on an even or odd numbered TV frame
;; bne @1
;; nop
;; nop
;; nop
;;@1:
;; rol tmp3   ; tmp3 bit 7 now in carry
;; lda RANDOM
;; rol        ; carry now in bit 0 of A
;; nop
;; nop
;; rts

; unsigned int __fastcall__ randi(void);
; NB cc65's rand() returns a positive signed int, meaning
; 0 to 0x7fff.
;;_randi:
;; jsr randb
;; and #$7f
;; tax
;; jsr randb
;; rts

; unsigned long __fastcall__ randl(void);
; this returns the full range of an unsigned long, 0 to 2**32-1
;;_randl:
;; jsr randb
;; sta sreg
;; jsr randb
;; sta sreg+1
;; jsr randb
;; tax
;; jsr randb
;; rts