最終更新:2011/09/17
SFC版DQ1,2で乱数を用いる場面では、すべて一種類の乱数によってカバーされている。
この乱数は乱数テーブル等は使用しておらず、常に計算によって生成している。
乱数が初期化されるタイミングは、電源投入(もしくはリセット)時と、
DQ2のみは冒険の書選択後の冒険開始時にももう一度リセットされる。
また、例外はかなりあるものの、基本的にはフレーム単位で生成され続けている。
(乱数が動かず、経過フレームのみが回っている場合もある。
また、1フレーム間に複数乱数が回る場合もある。)
乱数計算で使用するメモリーは、$7E0067・$7E00E0・$7E00E2(全て16bit)である。
$67:経過フレーム(1フレームごとにそれぞれ1増加)
※下位1バイト($67)は範囲[0〜0xFF]、上位バイト($68)は範囲[0〜0x04]で、
それぞれ別にカウントが行われている。(例:042eの次は002fになる。)
$E0:乱数の下位2バイト(計算にのみ用いる)
$E2:乱数の上位2バイト(いわゆる乱数。下位1バイトのみ、もしくは2バイト用いることもある)
(0x〜は16進数)
★簡潔に言うと、直前の乱数値($E3〜$E0の8桁)を乱数xとすると
乱数x’=乱数x×3+0x3549+$67 となり、線形合同法の亜種である。
実際に乱数として用いられるのは$E2部分で、1バイトもしくは2バイト分使用される。
8桁の乱数を33221100とすると、22または3322である。
05A550(0BA550)からが乱数生成ルーチンであるが、繰り上がり処理に一部例外が生じる。
05A550 | REP #$20 | ; Aを16bitモードに |
05A552 | LDA $E2 | ; "A" = $E2 |
05A554 | PHA | |
05A555 | LDA $E0 | ; "A" = $E0 |
05A557 | ASL $E0 | ; $E0×2 |
05A559 | ROL $E2 | ; $E2×2(キャリーフラグを含む) |
05A55B | CLC | ; キャリーフラグを0に |
05A55C | ADC $E0 | ; "A" += $E0 |
05A55E | STA $E0 | ; $E0 = "A" |
05A560 | PLA | |
05A561 | ADC $E2 | ; "A" += $E2 |
05A563 | STA $E2 | ; $E2 = "A" |
05A565 | LDA $E0 | ; "A" = $E0 |
05A567 | CLC | |
05A568 | ADC #$3549 | ; "A" += #$3549 |
05A56B | ADC $67 | ; "A" += $67 |
05A56D | STA $E0 | ; $E0 = "A" |
05A56F | LDA $E2 | ; "A" = $E2 |
05A571 | ADC #$0000 | ; "A" += 0 |
05A574 | STA $E2 | ; $E2 = "A" |
05A576 | SEP #$20 | ; Aを8bitモードに |
05A578 | LDA #$00 | ; "A" = 0 |
05A57A | XBA | |
05A57B | LDA $E2 | ; "A" = $E2 |
05A57D | RTL |
(乱数x’=乱数x×3+0x3549+$67)
これを見ると分かるかもしれないが、+0x3549の繰り上がり処理のみ特殊である。
×3した後の$E0部分(16bit)に加算するのだが、
加算後が16bit以上になった際は直接繰上りせず、
$67を足すときに一緒に、最下位bitに+1される。
05A565 | LDA $E0 | ; "A" = $E0 |
05A567 | CLC | ; キャリーフラグを0に |
05A568 | ADC #$3549 | ; "A" += #$3549 |
05A56B | ADC $67 | ; "A" += $67 |
まず、この言語の仕様として、
というものがあるが、
0x3549加算後にいわゆる繰り上がり処理がないため、
ADC $67でキャリーフラグが消化され、+1されてしまうのである。