assembly 从6502汇编程序中获取随机数

j8ag8udp  于 2023-06-23  发布在  其他
关注(0)|答案(8)|浏览(144)

尝试使用JSR $E09A在我的Commodore 64(C64)上生成一系列随机数,并从$63和$64中检索数字。(根据我所看到的所有文档,当你从BASIC中使用RND(0)时,它是同一个例程。但不能让它迭代。下面的代码将工作,并在自己执行时在$63和$64中放置不同的数字。

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

现在,当我尝试用下面的代码迭代10次时,它永远不会返回。

. 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

我是不是错过了什么太明显的东西了。我不担心它有多“随机”。在这一点上,我只想要一系列随机数字。

mzmfm0qo

mzmfm0qo1#

感谢Ross Ridge建议被调用的函数改变了Y寄存器中的值。我就知道这一定是显而易见的事情!
通过将Y存储在JSR之前,并在之后恢复,它现在将正确地迭代。以下是快速解决方案:
编辑:更新7/10/17 -显示完整的代码并纳入JeremyP的建议。这本质上是一个抛硬币迭代器(50000次重复),用于试验随机

.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

我可以在循环中通过LDA $63LDA $64获得随机数,并将其用于我的目的。
结果证明,这比预期的要慢得多,只花了BASIC中一半的时间。RND函数需要很多周期,但是,我发现这个Compute! article使用SID芯片作为随机数生成器。

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

一旦打开,它会独立生成数字,并且不必再次执行。重复调用LDA $D41B的循环将在每次迭代中获得一个新的随机数。在我的测试中,50,000次迭代耗时1.25秒,百万次迭代耗时24秒多一点。对于一台1 MHz的计算机来说,这是相当令人印象深刻的!

33qvvth1

33qvvth12#

您实际上是在调用RND(0),它使用计时器生成一个种子。然而,这在组装中不能直接使用。首先尝试切换到一个正数(任何数字),看看它是否开始产生值。

slhcrj9b

slhcrj9b3#

如果你没有定时光栅IRQ或类似的程序,你可以用lda $d012得到一个“随机”数。

px9o7tmv

px9o7tmv4#

我发现这个线程在C64汇编中搜索更通用的RND(开始,结束)例程。如以下BASIC示例所实现的:

INT(RND(1) * (end- start + 1)) + start

虽然这里有很多有用的答案,但我错过了这种解决方案,所以我必须找到自己的;这可能对另一个来这个帖子的人有帮助,所以这里是:

lda #<end   
            sta $FD
            lda #>end
            sta $FE
            lda #<start
            sta $FB
            lda #>start
            sta $FC
rnd:
            //reseed, to avoid repeated sequence; RND(0)
            lda #00
            jsr $E09A
            //++end 
            inc $FD
            bne skip1
            inc $FE
skip1:
            //- start
            lda $FD
            sec
            sbc $FB
            sta $FD
            lda $FE
            sbc $FC
            sta $FE         

            //++end-start to FAC
            ldy $FD
            lda $FE
            jsr $B391 //A(h),Y(L) - FAC 
            ldx #<flt
            ldy #>flt
            jsr $BBD4   //store FAC to flt
            //get actual RND(1)
            lda #$7f
            jsr $E09A
            //multiply by ++end - start
            lda #<flt
            ldy #>flt
            jsr $BA28
            //to integer
            jsr $BCCC
            //FAC to int;
            jsr $B1BF
            lda $65         
            clc
            adc $FB
            sta $14
            lda $64
            adc $FC
            sta $15
            rts     
flt:        .byte 0,0,0,0,0

该例程使用范围为0 - 32767的16位数字。争论始于251,252;结束于253,254.在$14中找到16位结果。

niknxzdl

niknxzdl5#

C64上的真实的问题是:

  1. SID生成的数字也是伪随机的,它们以序列的形式重复(我找不到讨论这个问题的链接)
    1.光栅位置不是随机的。
    c64中真正的随机性的唯一来源是用户输入。
    所以我要做的是:
    1.初始化SID噪声波形
    1.在启动时获取cia定时器1 LSB(这在普通c64上很好,但在仿真器上不是随机的)
    1.启动cia定时器2
    1.等待用户按下任意键(或操纵杆方向/按钮)
    1.获取cia定时器2 LSB
    1.获取SID幅度值
    1.可选地获取光栅位置,但取决于你是从basic还是汇编程序调用这个例程,你可能不会得到一个完全随机的值。
    然后,你有你最喜欢的伪随机例程的随机种子。或者只是一个16/24/32位随机数。
    在游戏中,例如,当用户移动操纵杆时,您可以获得cia计时器并获得随机字节。
    注意:在模拟器中删除prg或d 64与写入“load...”有很大的不同,因为每个用户每次写入的内容都不同,在这种情况下,计时器LSB是“随机的”。
    在一些仿真器中,由于这个原因,随机延迟被添加到计算机启动。
eeq64g8w

eeq64g8w6#

LDA #$FF
STA $D40E
STA $D40F
LDA #$81
STA $D412

JSR RND  ; A will contain a random byte
RTS

RND:
   LDA $DC04
   STA YY+1
XX:
   EOR #$FF
   STA ($fd),y
   EOR $D41B
YY:
   EOR #$00
   STA XX+1
   RTS
pxq42qpu

pxq42qpu7#

现在已经很晚了,但根据需求,您也可以自己滚动PRNG。有些算法很容易实现,例如,我将在这里使用参数[3,25,24]展示一个32位xorshift实现(因为这使得其中两个移位使用很少的代码)。返回的随机数有16位:

rnd_seed:
                sta     $22             ; store pointer to PRNG state
                stx     $23
                lda     #$00            ; initialize with 0
                ldy     #$03
rs_clrloop:     sta     ($22),y
                dey
                bne     rs_clrloop
                lda     $d012           ; except for LSB, use current raster
                bne     seed_ok
                lda     #$7f            ; or a fixed value if 0
seed_ok:        sta     ($22),y
                rts

rnd:
                sta     $22             ; store pointer to PRNG state
                stx     $23
                ldy     #$03
r_cpyloop:      lda     ($22),y         ; copy to ZP $fb - $fe
                sta     $fb,y
                dey
                bpl     r_cpyloop
                ldy     #$03            ; and shift left 3 bits
r_shiftloop:    asl     $fb
                rol     $fc
                rol     $fd
                rol     $fe
                dey
                bpl     r_shiftloop
                ldy     #$03
r_xorloop:      lda     ($22),y         ; xor with original state
                eor     $fb,y
                sta     ($22),y
                dey
                bpl     r_xorloop
                ldy     #$03
                lda     ($22),y
                lsr     a               ; MSB >> 1 gives ">> 25"
                ldy     #$00
                eor     ($22),y         ; xor with original state
                sta     ($22),y
                ldy     #$03            ; this is also value for "<< 24"
                eor     ($22),y         ; so xor with MSB
                sta     ($22),y
                tax                     ; use the two "higher" bytes as result ...
                dey
                lda     ($22),y         ; ... in A/X
                rts

使用示例:

main:
                lda     init
                bne     noinit
                lda     #<prng
                ldx     #>prng
                inc     init
                jsr     rnd_seed
noinit:         lda     #<prng
                ldx     #>prng
                jsr     rnd
                jmp     $bdcd        ; C64 BASIC routine output 16bit int in A/X

init:           .byte   $00
prng:           .res    4            ; 32bit PRNG state
mkh04yzy

mkh04yzy8#

SID芯片实际上可以产生比BASIC的伪随机数更随机的数字。启动发生器:

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
RTS

然后你可以随时获得随机数,只要你想:

LDA $D41B ; get random value from 0-255

相关问题