从 6502 汇编程序中获取随机数
Getting random number from 6502 assembler
尝试使用 JSR $E09A 在我的 Commodore 64 (C64) 上生成一系列随机数,并从 $63 和 $64 中检索数字。 (根据我看到的所有文档,当您使用 BASIC 中的 RND(0) 时,这是相同的例程。但无法对其进行迭代。以下将起作用,并在执行时在 $63 和 $64 中放置不同的数字本身。
. C000 A5 00 LDA [=10=]
. C002 20 9A E0 JSR $E09A
. C005 00 BRK
现在,当我尝试使用以下代码迭代 say 10 次时,它永远不会 returns。
. C000 A0 0A LDY #[=11=]A
. C002 A9 00 LDA #[=11=]
. C004 20 9A E0 JSR $E09A
. C007 88 DEY
. C008 D0 F8 BNE $C002
. C00A 00 BRK
我是不是漏掉了一些我看不到的明显东西。我不担心它有多“随机”。此时我只想要一系列随机数。
您实际上是在调用 RND(0)
,它使用计时器生成种子。但是,这不能直接用于组装。首先尝试切换到正数(任何数字)并查看它是否开始产生值。
感谢 提示被调用函数正在更改 Y 寄存器中的值。我知道这一定很明显!
通过在 JSR
之前存储 Y 并在之后恢复,它现在可以正确迭代。这是快速修复:
编辑:更新于 2017 年 7 月 10 日 - 以显示完整代码并纳入 建议。这本质上是一个硬币翻转迭代器(50000 次重复),用于随机试验
.C 033c A9 00 LDA #[=10=]
.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
.C 034f C9 80 CMP # ; >128 = HEADS
.C 0351 90 0D BCC 60 ; else continue loop
.C 0353 18 CLC ; increment 2 byte number
.C 0354 A5 FB LDA $FB
.C 0356 69 01 ADC # ; LSB
.C 0358 85 FB STA $FB
.C 035a A5 FC LDA $FC
.C 035c 69 00 ADC #[=10=] ; MSB
.C 035e 85 FC STA $FC
.C 0360 C6 FE DEC $FE
.C 0362 D0 E6 BNE 4A ; end inner loop
.C 0364 C6 FD DEC $FD
.C 0366 D0 DE BNE 46 ; end outer loop
.C 0368 60 RTS ; return to basic
我可以在循环中通过 LDA
或 LDA
获得随机数并将其用于我的目的。
事实证明这比预期的要慢很多,只用了 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 # ; noise waveform, gate bit off
STA $D412 ; voice 3 control register
一旦开启,它会独立生成数字,无需再次执行。重复调用 LDA $D41B
的循环将在每次迭代中为您提供一个新的随机数。在我的测试中,50,000 次迭代花费了 1.25 秒,而 100,000 次迭代花费了 24 秒多一点。对于 1MHz 的计算机来说相当令人印象深刻!
如果您没有带定时器光栅-IRQ 或类似程序的程序,您可以使用 lda $d012
获得一个 "random" 数字。
SID芯片实际上可以产生比BASIC的伪随机数更随机的数字。启动生成器:
LDA #$FF ; maximum frequency value
STA $D40E ; voice 3 frequency low byte
STA $D40F ; voice 3 frequency high byte
LDA # ; noise waveform, gate bit off
STA $D412 ; voice 3 control register
RTS
然后你可以随时获得随机数:
LDA $D41B ; get random value from 0-255
现在已经很晚了,但根据需要,您也可以推出自己的 PRNG。有些算法实现起来很简单,例如,我将在此处使用参数 [3,25,24]
展示一个 32 位 xorshift 实现(因为这使得两个班次使用非常少的代码)。返回的随机数有16位:
rnd_seed:
sta ; store pointer to PRNG state
stx
lda #[=10=] ; initialize with 0
ldy #
rs_clrloop: sta (),y
dey
bne rs_clrloop
lda $d012 ; except for LSB, use current raster
bne seed_ok
lda #f ; or a fixed value if 0
seed_ok: sta (),y
rts
rnd:
sta ; store pointer to PRNG state
stx
ldy #
r_cpyloop: lda (),y ; copy to ZP $fb - $fe
sta $fb,y
dey
bpl r_cpyloop
ldy # ; and shift left 3 bits
r_shiftloop: asl $fb
rol $fc
rol $fd
rol $fe
dey
bpl r_shiftloop
ldy #
r_xorloop: lda (),y ; xor with original state
eor $fb,y
sta (),y
dey
bpl r_xorloop
ldy #
lda (),y
lsr a ; MSB >> 1 gives ">> 25"
ldy #[=10=]
eor (),y ; xor with original state
sta (),y
ldy # ; this is also value for "<< 24"
eor (),y ; so xor with MSB
sta (),y
tax ; use the two "higher" bytes as result ...
dey
lda (),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 [=11=]
prng: .res 4 ; 32bit PRNG state
我发现此线程在 C64 汇编中搜索更通用的 RND(start, end) 例程。作为这个 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 #f
jsr $E09A
//multiply by ++end - start
lda #<flt
ldy #>flt
jsr $BA28
//to integer
jsr $BCCC
//FAC to int;
jsr $B1BF
lda
clc
adc $FB
sta
lda
adc $FC
sta
rts
flt: .byte 0,0,0,0,0
例程使用 0 - 32767 范围内的 16 位数。参数从 251,252 开始;以 253、254 结尾。
在 $14 中找到 16 位结果。
C64 的真正问题是:
SID 生成的数字也是伪随机的,它们按顺序重复(我找不到 link 讨论这个)
光栅位置不是随机的。
c64 中真正随机性的唯一来源是用户输入。
所以我做的是:
- 初始化SID噪声波形
- 在启动时获取 cia 定时器 1 LSB(这在普通的 c64 上很好,但在模拟器上不是随机的)
- 启动 cia 计时器 2
- 等待用户按下任意键(或操纵杆 direction/button)
- 获取 cia 定时器 2 LSB
- 获取SID振幅值
- 可选择获取光栅位置,但取决于您是从基本程序还是汇编程序调用此例程,您可能无法获得完全随机的值。
然后你就有了你最喜欢的伪随机例程的随机种子。或者只是一个 16/24/32 位随机数。
例如,在游戏中,当用户移动操纵杆并获得随机字节时,您可以获得 cia 计时器。
注意:在模拟器中删除 prg 或 d64 与编写“load...”非常不同,因为每个用户每次的写法都不同,并且定时器 LSB 在这种情况下是“随机的”。
出于这个原因,在某些模拟器中,计算机启动会添加随机延迟。
尝试使用 JSR $E09A 在我的 Commodore 64 (C64) 上生成一系列随机数,并从 $63 和 $64 中检索数字。 (根据我看到的所有文档,当您使用 BASIC 中的 RND(0) 时,这是相同的例程。但无法对其进行迭代。以下将起作用,并在执行时在 $63 和 $64 中放置不同的数字本身。
. C000 A5 00 LDA [=10=]
. C002 20 9A E0 JSR $E09A
. C005 00 BRK
现在,当我尝试使用以下代码迭代 say 10 次时,它永远不会 returns。
. C000 A0 0A LDY #[=11=]A
. C002 A9 00 LDA #[=11=]
. C004 20 9A E0 JSR $E09A
. C007 88 DEY
. C008 D0 F8 BNE $C002
. C00A 00 BRK
我是不是漏掉了一些我看不到的明显东西。我不担心它有多“随机”。此时我只想要一系列随机数。
您实际上是在调用 RND(0)
,它使用计时器生成种子。但是,这不能直接用于组装。首先尝试切换到正数(任何数字)并查看它是否开始产生值。
感谢
通过在 JSR
之前存储 Y 并在之后恢复,它现在可以正确迭代。这是快速修复:
编辑:更新于 2017 年 7 月 10 日 - 以显示完整代码并纳入
.C 033c A9 00 LDA #[=10=]
.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
.C 034f C9 80 CMP # ; >128 = HEADS
.C 0351 90 0D BCC 60 ; else continue loop
.C 0353 18 CLC ; increment 2 byte number
.C 0354 A5 FB LDA $FB
.C 0356 69 01 ADC # ; LSB
.C 0358 85 FB STA $FB
.C 035a A5 FC LDA $FC
.C 035c 69 00 ADC #[=10=] ; MSB
.C 035e 85 FC STA $FC
.C 0360 C6 FE DEC $FE
.C 0362 D0 E6 BNE 4A ; end inner loop
.C 0364 C6 FD DEC $FD
.C 0366 D0 DE BNE 46 ; end outer loop
.C 0368 60 RTS ; return to basic
我可以在循环中通过 LDA
或 LDA
获得随机数并将其用于我的目的。
事实证明这比预期的要慢很多,只用了 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 # ; noise waveform, gate bit off
STA $D412 ; voice 3 control register
一旦开启,它会独立生成数字,无需再次执行。重复调用 LDA $D41B
的循环将在每次迭代中为您提供一个新的随机数。在我的测试中,50,000 次迭代花费了 1.25 秒,而 100,000 次迭代花费了 24 秒多一点。对于 1MHz 的计算机来说相当令人印象深刻!
如果您没有带定时器光栅-IRQ 或类似程序的程序,您可以使用 lda $d012
获得一个 "random" 数字。
SID芯片实际上可以产生比BASIC的伪随机数更随机的数字。启动生成器:
LDA #$FF ; maximum frequency value
STA $D40E ; voice 3 frequency low byte
STA $D40F ; voice 3 frequency high byte
LDA # ; noise waveform, gate bit off
STA $D412 ; voice 3 control register
RTS
然后你可以随时获得随机数:
LDA $D41B ; get random value from 0-255
现在已经很晚了,但根据需要,您也可以推出自己的 PRNG。有些算法实现起来很简单,例如,我将在此处使用参数 [3,25,24]
展示一个 32 位 xorshift 实现(因为这使得两个班次使用非常少的代码)。返回的随机数有16位:
rnd_seed:
sta ; store pointer to PRNG state
stx
lda #[=10=] ; initialize with 0
ldy #
rs_clrloop: sta (),y
dey
bne rs_clrloop
lda $d012 ; except for LSB, use current raster
bne seed_ok
lda #f ; or a fixed value if 0
seed_ok: sta (),y
rts
rnd:
sta ; store pointer to PRNG state
stx
ldy #
r_cpyloop: lda (),y ; copy to ZP $fb - $fe
sta $fb,y
dey
bpl r_cpyloop
ldy # ; and shift left 3 bits
r_shiftloop: asl $fb
rol $fc
rol $fd
rol $fe
dey
bpl r_shiftloop
ldy #
r_xorloop: lda (),y ; xor with original state
eor $fb,y
sta (),y
dey
bpl r_xorloop
ldy #
lda (),y
lsr a ; MSB >> 1 gives ">> 25"
ldy #[=10=]
eor (),y ; xor with original state
sta (),y
ldy # ; this is also value for "<< 24"
eor (),y ; so xor with MSB
sta (),y
tax ; use the two "higher" bytes as result ...
dey
lda (),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 [=11=]
prng: .res 4 ; 32bit PRNG state
我发现此线程在 C64 汇编中搜索更通用的 RND(start, end) 例程。作为这个 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 #f
jsr $E09A
//multiply by ++end - start
lda #<flt
ldy #>flt
jsr $BA28
//to integer
jsr $BCCC
//FAC to int;
jsr $B1BF
lda
clc
adc $FB
sta
lda
adc $FC
sta
rts
flt: .byte 0,0,0,0,0
例程使用 0 - 32767 范围内的 16 位数。参数从 251,252 开始;以 253、254 结尾。 在 $14 中找到 16 位结果。
C64 的真正问题是:
SID 生成的数字也是伪随机的,它们按顺序重复(我找不到 link 讨论这个)
光栅位置不是随机的。
c64 中真正随机性的唯一来源是用户输入。
所以我做的是:
- 初始化SID噪声波形
- 在启动时获取 cia 定时器 1 LSB(这在普通的 c64 上很好,但在模拟器上不是随机的)
- 启动 cia 计时器 2
- 等待用户按下任意键(或操纵杆 direction/button)
- 获取 cia 定时器 2 LSB
- 获取SID振幅值
- 可选择获取光栅位置,但取决于您是从基本程序还是汇编程序调用此例程,您可能无法获得完全随机的值。
然后你就有了你最喜欢的伪随机例程的随机种子。或者只是一个 16/24/32 位随机数。
例如,在游戏中,当用户移动操纵杆并获得随机字节时,您可以获得 cia 计时器。
注意:在模拟器中删除 prg 或 d64 与编写“load...”非常不同,因为每个用户每次的写法都不同,并且定时器 LSB 在这种情况下是“随机的”。
出于这个原因,在某些模拟器中,计算机启动会添加随机延迟。