Search code examples
assemblyrandom6502c64

Getting random number from 6502 assembler


Trying to generate a series of random numbers on my Commodore 64 (C64) using JSR $E09A and retrieving the number from $63 and $64. (which according to all the documentation I've seen is the same routine when you use RND(0) from BASIC. But can't get it to iterate. The following will work and place a different number in $63 and $64 when executed by itself.

. C000  A5 00    LDA $00
. C002  20 9A E0 JSR $E09A
. C005  00       BRK

Now when I try to iterate say 10 times with the following code, it never returns.

. C000  A0 0A    LDY #$0A
. C002  A9 00    LDA #$00
. C004  20 9A E0 JSR $E09A
. C007  88       DEY
. C008  D0 F8    BNE $C002
. C00A  00       BRK

Am I missing something so obvious I can't see it. I'm not worried about how "random" it is. At this point I just want a series of random numbers.


Solution

  • Thanks to Ross Ridge for suggesting that the called function was changing the value in the Y register. I knew it had to be something obvious!

    By storing Y before the JSR, and restoring after, it now will iterate properly. Here is the quick fix:

    Edit: Updated 7/10/17 - to show full code and incorporate JeremyP suggestion. This is essentially a coin flip iterator (50000 repetitions) for purposes of experimenting with random

    .C 033c  A9 00       LDA #$00
    .C 033e  85 FB       STA $FB    ; set up register for counter
    .C 0340  85 FC       STA $FC
    .C 0342  A2 C8       LDX #$C8   ; outer loop= 200
    .C 0344  86 FD       STX $FD
    .C 0346  A0 FA       LDY #$FA   ; inner loop=250
    .C 0348  84 FE       STY $FE
    .C 034a  20 94 E0    JSR $E094  ; Get random# Vic20 Address (E09B for C64)
    .C 034d  A5 63       LDA $64
    .C 034f  C9 80       CMP #$80   ; >128 = HEADS
    .C 0351  90 0D       BCC $0360  ; else continue loop
    .C 0353  18          CLC        ; increment 2 byte number
    .C 0354  A5 FB       LDA $FB
    .C 0356  69 01       ADC #$01   ; LSB
    .C 0358  85 FB       STA $FB
    .C 035a  A5 FC       LDA $FC
    .C 035c  69 00       ADC #$00   ; MSB
    .C 035e  85 FC       STA $FC
    .C 0360  C6 FE       DEC $FE
    .C 0362  D0 E6       BNE $034A  ; end inner loop
    .C 0364  C6 FD       DEC $FD
    .C 0366  D0 DE       BNE $0346  ; end outer loop
    .C 0368  60          RTS        ; return to basic
    

    I can get the random number by LDA $63 or LDA $64 inside the loop and use it for my purposes.

    This turned out to be a lot slower than expected, taking only half the time it would've taken in BASIC. The RND function takes a lot of cycles, however, I found this Compute! article which uses the SID chip as a random number generator.

    LDA #$FF  ; maximum frequency value
    STA $D40E ; voice 3 frequency low byte
    STA $D40F ; voice 3 frequency high byte
    LDA #$80  ; noise waveform, gate bit off
    STA $D412 ; voice 3 control register  
    

    Once turned on it generates numbers independently and doesn't have to be executed again. A loop that repeatadly calls LDA $D41B will get you a new random number on each iteration. In my test 50,000 iterations took 1.25 seconds and million took a little over 24 seconds. Pretty impressive for a 1MHz computer!