オリジナルCPU「DL166」の命令セットの動作を確認する:オリジナルCPUでバイナリコード入門(7)(1/3 ページ)
オリジナル4ビットCPUを用いてバイナリコードを学ぶ本連載。第7回は、「Tang Nano 9K」に移植したオリジナルCPU「DL166」の命令セットの動作を確認する。
はじめに
今回は、これまで「Tang Nano 9K(以下、Tang Nano)」に移植してきたオリジナル4ビットCPU「DL166」の命令セットの動作確認を行っていきます。この動作確認が終わることで、次回からTang Nanoを用いた実機によるバイナリコーディングの演習に移行できます。
なお、現時点でTang Nanoを搭載した演習教材には入力UIが存在しませんが、初代の「DL166」向けに設計したトレーナーを原型とした案を構想中です。ただし、キースイッチを用いていたため物理的な小型化が困難という課題があり、現在はキースイッチ以外を使うことを検討しています。
⇒連載「オリジナルCPUでバイナリコード入門」バックナンバー
命令セットの動作を確認するコードを書こう
ここからアセンブラ表記を用いて、DL166の命令セットの動作を確認するソースコードを試していきましょう。ソースコードはDL166を実装したコードとは別のファイルにコーディングします。試したいアセンブラのソースコードを動作させたい場合は、GitHubで公開しているinstructons.vの30行目のinclude文の後のファイル名のみを書き換えてください。テストするソースコードも同じディレクトリにありますので、以下のリンクから参照してください。
これはDL166の実装コードには極力手を触れず、DL166で動作するバイナリコーディングに集中できるようにするための工夫です。これまでTang Nano搭載のFPGAの製造元であるGOWIN Semiconductorの開発環境(IDE)を用いてVerilog-HDLによるCPUの開発をしてきたのと同様に、シンセサイズ(Synthesize)、配置と配線(Place & Route)、ここまでエラーが出てなければ生成されたコンフィギュレーションを実機に書き込みます。そすれば、作成したアセンブラのコードがTang Nanoの実機上で実行されます。
なお、ここから紹介する各命令セットの概要については、DL166の仕様を紹介した第1回記事でも触れているのでご参照ください。
また、紹介するサンプルプログラムを実行した後のレジスタの値については、連載第6回『オリジナルCPU「DL166」のレジスタをLEDドットマトリックスで見える化する』の「LEDドットマトリックスの見方」をご参照ください。
ADDの動作を試すコード
ADDはオバーフローのフラグに影響を与える命令セットです。これは筆者の持論なのである程度聞き流してほしいのですが、この手のフラグをセットしなければならないのはCPUが想定した閉じた世界を逸脱したことをユーザーに知らせるためです。ある演算が閉じた世界を逸脱したとは、逆演算を行ったとしても元に戻らないということです。ADDはいわゆる加算であり、人間の世界ではとても親しみのある演算なのですが、コンピュータにとっては厄介な演算なのかもしれません。
add.asm(リスト1)はADDの動作を試すコードです。最初に紹介するコードなので記述内容を少し詳しく説明しましょう。1行目のram[0]は命令セットが格納されているメモリを示しており、その次にそのアドレスに代入する値を記述しています。今回紹介するコードでは、8ビット長の2進数形式で書いています。途中アンダーバーが入っていますが、これは命令セットを区切って見やすくするためのもので、あってもなくてもエラーは起こりません。また、各行末尾の“//”以下はコメントです。add.asm内で出てくる、mvi、mov、jmpについては今回の記事の後半で紹介します。
ram[0] <=8'b1010_0010; // mvi r0,2 ram[1] <=8'b00_001_000; // mov r1,r0 ram[2] <=8'b1010_0011; // mvi r0,3 ram[3] <=8'b01_000_001; // add r1 ram[4] <=8'b1001_0100; // jmp 4
add2.asm(リスト2)は、ADDにオバーフローを起こさせる例で、キャリーフラグが立つ状況を起こさせるためのコードになっています。キャリーフラグがセットされたことは、Tang Nanoのボード上にある6個のオレンジ色のLEDが点灯することによってキャリーフラグの値を確認できます。LEDが点灯している場合は、キャリーフラグの値が1であることを示しています。
ram[0] <=8'b1010_1000; // mvi r0,8 ram[1] <=8'b00_001_000; // mov r1,r0 ram[2] <=8'b1010_1001; // mvi r0,9 ram[3] <=8'b01_000_001; // add r1 ram[4] <=8'b1001_0100; // jmp 4
add2.asmの3番地のアドレス(ram[3])のADDが実行される前の状態をおさらいしておきましょう。R0が9、R1が8です。これらが加算されるので15を超えてしまいます。これらの加算された値はR0に代入されることになっているのですが、4ビットの枠をはみ出してしまいます。これがいわゆるオーバーフローで、起きた場合にキャリーフラグを1にします。この値は先述したようにLEDを点灯させるので、皆さんの目で確認してみてください。最後の行のJMP命令により、コードの実行はその場で停止しているように見えます。
ORの動作を試すコード
ORは指定されたレジスタの値とR0の値の論理和を取ってR0に代入します。以下のor.asm(リスト3)で示すコードは、R1の値の0101とR0の値の1010の論理和を取ってR0に格納します。
ram[0] <=8'b1010_0101; // mvi r0,5 ram[1] <=8'b00_001_000; // mov r1,r0 ram[2] <=8'b1010_1010; // mvi r0,a ram[3] <=8'b01001_001; // or r1 ram[4] <=8'b1001_0100; // jmp 4
演算結果は1111となりR0に格納されます。
日本の路線バスはおそらくどこでも、乗客が運転手に停留所での降車を知らせるとき、席の近くにある押しボタンスイッチを押しますよね。しかし海外の路線バスでは、運転手の近くに引くとオンになるスイッチがあって、それにワイヤがつながってて一番後ろの席までそのワイヤが延びているんですね。降車したい客はそのワイヤを引っ張って知らせるわけです。1人が引っ張っても、同時に複数の乗客が引っ張ってもこのシステムは動作します。これは、まさに日常の生活における論理和を実感した体験でした。
Copyright © ITmedia, Inc. All Rights Reserved.