メモ帳などの等幅フォント・固定幅で読んでください。タブ半角8桁で、半角で80桁、全角40桁以上無いと読み辛いと思います。 『Z80の乱数』 by baa 2013/12 2014/01 加筆修正 ● 乱数とは何か?  広辞苑によると  (数)0から9までの数字から無作為復元抽出を繰り返して得られる数の列。完全に無秩序で、かつ全体としては出現の頻度が等しい。  だそうです。私の知る「乱数」の作り方は、例えば0から9を書いた20面有るサイコロを振ります。サイコロが正確で、振り方に癖が無いなら、振って出た目を順に並べたものが「乱数」です。  「乱数」を作る場合は何度もサイコロを振るので、1ばかり連続5回というように、同じ目が続くかもしれませんし、100回振っても全然出ない目が有るかもしれません。それが「乱数」です。  このサイコロの方法は、実際に実務に使われている乱数の生成方法ですが、この方法で「乱数」を作ると、広辞苑の「・・・全体としては出現の頻度が等しい」の部分と違う結果になるような気がします。  「・・・全体としては出現の頻度が等しい」のは、「乱数」の一つ一つの数の出方をどうにか細工した場合なのでは?? あるいは「繰り返す回数が多いほど、全体として出現の頻度が同じ値に近づく」とか書いたほうが良かったりして? ● 8ビットパソコンの乱数 参照した本:3番と4番の方法『Z80マシン語秘伝の書』       5番の方法『MSXマシン語入門講座』(←ソースを変更してあります)  RPGの敵の出現率や攻撃の成功率など、値に変化を持たせたい場合等に「乱数」を使います。コンピュータで作る「乱数」は、元にする任意の値 = 「種」から適当な式(や手順)を使って作った値ですので、種が同じなら値も同じです。よって、無秩序では無く「擬似乱数」と呼ばれます。  パソコンで「擬似乱数」を作るのには、色々な方法が有ると思いますが、普通はプログラミング言語に「擬似乱数」を作る関数が用意されているので、それを使います。でも、アセンブリ言語の場合は、自分で作らなければなりません。そこで、乱数の作り方を書きます。私が知っているのは、8ビットCPUのZ80で「擬似乱数」を作る方法です。簡単且つ用途限定 〜 複雑且つ多用途まで幾つかありますので、ご紹介します。  1.リフレッシュレジスタの値をそのまま使う    普通のメモリ(DRAM)は、一定期間内に再書き込みしないと値が消えます。   期間内に再書き込みして値を保持する仕組み = リフレッシュが必要です。    Z80には、メモリをリフレッシュするタイミングを常にカウントしている、   Rレジスタ(リフレッシュレジスタ)があります。    Rレジスタは、8ビットの内の7ビットを使用して、常に0から順に1ずつ   127まで増えては0に戻ってまた増えていく、ということを繰り返しています。   メモリを512バイトずつリフレッシュするので、65536÷512=128   というわけです(0が入るので0〜127です)。    ゲームをする人がボタンを押すタイミング(注1)のような、機械の時間から   見ればバラバラのタイミングで、Rレジスタの値を調べれば、0から127の   乱数となります。また、下記4種類の方法の「種」としても使えます。   例) LD A,R ; Rレジスタノ アタイガAニハイル   注1.ボタンでバラバラのタイミングを得るのは、結構難しいです。なぜなら      普通のゲームはちゃんとウエイトを入れて、毎回の全体のループの同じ      所で入力チェックするからです。タイトル画面やアドベンチャーゲーム      で、音楽以外何もせず、ひたすら入力待ちループしてる時だといけるか      もしれません? 実際に、この方法を拙作『大魔猫』で使っています  2.P/Vフラグを使う“丁半”乱数    1バイトのワークを用意しておいて、タイマー割り込みでも、サブルーチン   (今で言うプロシージャ)を呼び出した時でも、使う前に1増やします。    255になれば、次は0に戻りますが、問題ありません。    必要な時に、その値をAに読んで OR A します。そしてP/Vフラグの   値を2値の擬似乱数(?)として使います (実際には条件ジャンプです)。    P/VフラグのPは、機器間の通信のパリティチェックに使う目的だと記憶   していますが(?)、論理演算等した後のAレジスタのビットの1の数が偶数   なら立つのです(注2)。以下は0から29までの OR A 結果です。    演算結果 Pフラグ   演算結果 Pフラグ   演算結果 Pフラグ    00000000 1       00001010 1       00010100 1    00000001 0       00001011 0       00010101 0    00000010 0       00001100 1       00010110 0    00000011 1       00001101 0       00010111 1    00000100 0       00001110 0       00011000 1    00000101 1       00001111 1       00011001 0    00000110 1       00010000 0       00011010 0    00000111 0       00010001 1       00011011 1    00001000 0       00010010 1       00011100 0    00001001 1       00010011 0       00011101 1    敵から逃げられるか? 先に攻撃するのはどちらか? 等々。そのままでなく   とも変化の「幅」に使えたり、2値の「擬似乱数」?って結構使えますよ。 LD A,(SEED) ; タネヲ Aニ ヨム INC A ; 1フヤス LD (SEED),A ; ジカイヨウニ タネヲ ホゾン OR A JP PO,HIT ; キスウ ナラ HITヘ トブ. グウスウデトブナラ JP PE,HIT トスル : SEED: DEFB 0 ; タネノ ワーク   注2.ビットの1の数が偶数なら立つのを、値が偶数なら立つと記述した本      を知っています。Z80(や、Z80アセンブリ言語)の解説本で、      その種の学校の先生が書いている本なのにです。タイポとかケアレス      ミスとかじゃない書き方でです。実際に本格的なプログラムを作れば      一発で気付きそうだけど・・・。有名な偉い先生の言っている事でも      間違いや誤解が有るかもしれないので、入門書でも解説書でも複数の      本を読んで、一々自分で試して確認したほうが良いでしょう  3.1バイト枠内で、種を5倍して1を足す    式で「擬似乱数」を作る方法の内、最も簡単で速いもので、0〜255の値   となります。偶数奇数が交互し、256回で一順し、下位4ビットは16回で   一順します。Aレジスタに結果が入ります。下のSEEDの所に「種」の数値を   書きます。 RND: LD A,SEED LD E,A ADD A,A ADD A,A ADD A,E INC A LD (RND+1),A ; ケッカヲ ジカイノ タネニスル RET  4.2バイト枠内で、種を5倍して16ビット値を足す    3番よりも、もっと本格的な「擬似乱数」が必要な場合は、同じく3の方法を   2バイトで計算します。65536回で1順します。SEEDの所に「種」を書   き、NUMBERの所には、0以外の任意の16ビット値を入れます。1バイト   の乱数が欲しい場合は、結果の上位をとって使います。大抵の場合はこれで事足   りるでしょう。 RND16: LD HL,SEED LD D,H LD E,L ADD HL,HL ADD HL,HL ADD HL,DE LD DE,NUMBER ADD HL,DE LD (RND16+1),HL ; ケッカヲ ジカイノ タネニスル ; LD A,H ; モシ 1バイトチ ガホシイナラ ジョウイヲ ツカウ RET  5.M系列の理論を使う    数学が分からないので理論は理解してません。アセンブリ言語で行う場合は、   2以上の任意の桁がある0以外の値のビット列を「種」とします。仮に8ビット   の列として、それを1ビット左にシフトして、はみ出たビットを0番以外の任意   (ただし一定)ビットとXORし、その結果を0番に入れます。    自分でも、何を言っているのか分かりません(^^;)。    もっと具体的に「8ビットの列を左に1ビットシフト。はみ出したビットを   5番とXORし、0番に入れる」という例を図にすると               ← 最初に左に1ビットシフト    8ビットの列   C 7 6 5 4 3 2 1 0             │     ↓         ↑             └────XOR────────┘    16ビット列で実装してみる(結果はHLに入る。63458回で一巡) RNDM: LD HL,SEED ADD HL,HL ; ヒダリシフト LD A,H ; H ヲ Aニ コピー JP NC,NOXOR ; ハミダシタ ビットガ0ナラ ジャンプ XOR 2 ; 00000010 ツマリ ビット1ト XOR NOXOR: BIT 1,A ; ビット1ハ タッテイルカ? (HLデノ ビット9) JP NZ,NOINC INC L NOINC: LD (RNDM+1),HL ; ケッカヲ ジカイノ タネニスル RET   注:理論では65535回で一順するらしいですが、本のリスト通りだと、     434回で1順してしまうので、↑自分なりに変更、高速化しました。     しかし63458回で1順します。本の手順説明がおかしいようです。    参考:本の通りのリスト RANDOM: PUSH AF PUSH BC XOR A LD C,A LD HL,(RND) ADD HL,HL RLA BIT 2,H JR Z,RAN1 INC C RAN1: XOR C JR Z,RAN2 INC HL RAN2: LD (RND),HL POP BC POP AF RET RND: DEFW 0100H