マインクラフトは手ごろなCPUエミュ
ロマンを求めて
CPUを自作してみたいと思ったら、ロジックICを組み合わせて作るのが普通かと思うのですが、そこまでするお金と気力がありませんでした。そこで使ったのがマインクラフトというゲームです。もとよりさんざん遊んでいたこのゲーム、実はゲーム内で論理回路を作ることができます。論理回路というよりは順序回路ですが。
ということで、ゲーム内でCPUを作ってみました。いつか作ってみたいと思っていたんですよね。
全体像がこちら。
4bitアーキテクチャです。とりあえず一個作ろうと思って作ったものです。一回作るとコツが見えてくるので、二回目またすぐ作ることにしました。それがこちら。
こちらは8bitアーキテクチャです。明らかに巨大化しています。一つ目は制作に一週間かかったのに、2つ目は2~3日程度で完成しました(結局はコピペで同じモジュールの量産なので規模自体はあんまり関係ない)。仕組みの解説はこっちでやります。一個目はアーキテクチャがあまりにもアレだったので...
2個目の方のアーキテクチャがこちら。図が汚いけど我慢してください。
とりあえず動けばよいという構成です。
仕組み
CPUを構成している部品は主にこれら8つのユニットです。
・プログラムメモリ
・命令カウンタ
・命令デコーダ
・Wレジスタ
・RAM汎用レジスタ
・ALU
・条件分岐
・GPIO(General Purpose Input/Output)
一つづつ解説します。
・プログラムメモリ
一番サイズ的に大きなユニットです。いわゆる機械語をここに順番に書きこんでいきます。1命令14bitとなっています。00011010011010みたいな感じで並べていきます。レッドストーントーチというブロックを手作業で一つづつ設置してビットを立ち上げていく必要があるので、プログラムの書き込みはかなりの重労働です。おかげでこのCPUは機能としてはいろいろできる割に、書き込むのが大変すぎて一番長いコードでも8byte型の足し算くらいしかしていません。かわいそう
・命令カウンタ
プログラムメモリから読み出す命令のアドレスをカウントする部分です。ジャンプ命令が来ると、指定のアドレスまで飛びます。また、条件分岐の命令で評価が真だった場合は条件分岐のモジュールから信号が飛んできて、それをトリガにして命令を1つ飛ばします。飛んだ先にジャンプ命令を置いておくことで、if文みたいな動作をできます。
・命令デコーダ
プログラムメモリから吐き出された命令を各モジュールへと分配する回路です。要はただのデマルチプレクサなわけですが。上3桁の数値をアドレスにして分けています。
・Wレジスタ
いわゆるレジスタです。RとLとの二つの1byte分のメモリがあり、それぞれ読み書きができます(以後WR、WLと表記)。これがモジュール間でデータをやりとりするためのバッファとなっています。
・RAM汎用レジスタ
ランダムアクセスメモリ、通称RAMです。よくPCのスペックに書かれているアレです。CPUの部品の一つとして数えるのはちょっと違うような気もしますが必ず必要なので。
レジスタです。作った時は勘違いしてましたが、アドレスを変数で管理できないのでこれは汎用レジスタとなります。
WRもしくはWLの値を読んで格納したり、またWRもしくはWLに書き込めます。容量はなんと16byte分しかありません。これでも一個目のCPUより4倍容量が増えたんです。
追記(2022.6.3)
次世代CPUを作成中ですが、こちらにはレジスタとは別に256byteのRAMを搭載予定です。いわゆるポインタが利用できるようになるので、配列を用いた繰り返し処理が可能になる予定です。
256byteRAM pic.twitter.com/tkAz4BeqAv
— あぐちゃんさん❄️ (@Agchan_Luice) 2022年6月2日
・ALU
ALUはArithmetic and Logic Unit(算術論理演算装置)の略で、CPUが実際に計算を行う部分です。Wレジスタの値を使って計算します。結果はWRに格納されます。機能は以下7つです。
・NOT
・AND
・OR
・XOR
・右シフト
・左シフト
・加算
シフトや加算でオーバーフローが発生すると、WLの最下位ビットが立ち上がります。
・条件分岐
いわゆるif文を実現するための回路です。Wレジスタの値を使って評価します。使える条件は以下3つです。
・WRとWLの完全一致
・WRが正か負か(最上位ビットが立ち上がっているか否か)
・0以外か
評価した結果が真になると、前述したように命令カウンタに信号が送られて、一つ命令が飛ばされます。偽だった場合には何も起こりません。
・GPIO(General Purpose Input/Output)
GPIOは汎用入出力ポートです。指定した番号の入力ポートの値がWRに格納できます。また、指定した番号の出力ポートにWRの値を出力できます。これで外部との値のやり取りが可能になります。その気になればディスプレイとかキーボードを接続とかも可能ではあるのですが、動作がめちゃくちゃ遅いのは自明です。