自作CPU Advent Calendar 2022 24日目
「オープンソースハードウェア」Foolish4 の互換機「LiarCPU」を作ってみました。
名前の由来
Foolish4 の 「foolish」には「ばかげた」という意味があります。
「ばか」「4」といえば…そう、4月馬鹿、エイプリルフールですね!
そして、エイプリルフールといえば嘘ですね!
というわけで、「嘘」を名前に取り入れました。
設計
Foolish4 の回路図は、紹介ページで公開されています。
そこで、これに沿った構造にしつつ、自分のクロック・プログラムROMインターフェイスに対応した構造にしました。
Foolish4 は、4ビットのプログラムカウンタと、1個の4ビットレジスタ、そして1個のフラグレジスタ (1ビット) を持ちます。
命令は8ビットで、下位4ビットがプログラムカウンタに設定する値および加算器の入力として用いられ、上位4ビットは動作を決定するROMのアドレスの一部を構成します。
さらに、加算器により命令の下位4ビットと4ビットレジスタの値の和を計算し、結果の下位4ビットを4ビットレジスタに設定する値、キャリーをフラグレジスタに設定する値とします。
動作を決定するROMは、命令の上位4ビットにフラグレジスタを加えた計5ビットのアドレスを用いてアクセスし、プログラムカウンタ・4ビットレジスタ・フラグレジスタの値を「設定する値」に設定するか、それとも現在の値を保持する (プログラムカウンタは1進める) かをそれぞれ制御します。
Foolish4 には、入出力ポートがありません。
LiarCPU では、出力ポートとして4ビットレジスタの値を出力することにしました。
回路図
KiCad で回路図を作成しました。
Foolish4 の動作の解析
Foolish4 について、紹介ページでは
すべての技術情報を公開しています。
と主張しています。
しかし、残念ながら、肝心の命令セットは無料で公開されているのを見つけることはできませんでした。
さらに、命令セットを回路から解析しようにも、動作の決定にはROMが使われており、このROMの中身についても無料で公開されているのを見つけることはできませんでした。
(販売されている書籍には載っていました)
そこで、紹介ページに掲載されている以下の動画をもとに、ROMのデータを解析してみました。
Foolish4 - YouTube
まず、動画の再生を少しずつ進めながら、LEDの点灯パターンを書き出しました。
点灯を1、消灯を0で表しました。
結果は、以下のようになりました。
----- DECODER ----
A3-A0 D7-D0 AREG CY COUNTER AREG FLAGS
0100 0011 0011 1000 1 1 0 0
0011 0001 1111 1000 1 0 1 1
0100 0011 0011 0111 1 1 0 0
0011 0001 1111 0111 1 0 1 1
0100 0011 0011 0110 1 1 0 0
0011 0001 1111 0110 1 0 1 1
0100 0011 0011 0101 1 1 0 0
0011 0001 1111 0101 1 0 1 1
0100 0011 0011 0100 1 1 0 0
0011 0001 1111 0100 1 0 1 1
0100 0011 0011 0011 1 1 0 0
0011 0001 1111 0011 1 0 1 1
0100 0011 0011 0010 1 1 0 0
0011 0001 1111 0010 1 0 1 1
0100 0011 0011 0001 1 1 0 0
0011 0001 1111 0001 1 0 1 1
0100 0011 0011 0000 1 1 0 0
0011 0001 1111 0000 1 0 1 1
0100 0011 0011 1111 0 0 0 0
0101 0001 0001 1111 0 0 1 1
0110 0010 0000 0000 1 1 0 0
0000 0001 0001 0000 1 0 1 1
0001 0100 0000 0001 0 1 0 0
0000 0001 0001 0001 0 0 1 1
0001 0100 0000 0010 0 1 0 0
0000 0001 0001 0010 0 0 1 1
0001 0100 0000 0011 0 1 0 0
0000 0001 0001 0011 0 0 1 1
0001 0100 0000 0100 0 1 0 0
0000 0001 0001 0100 0 0 1 1
0001 0100 0000 0101 0 1 0 0
0000 0001 0001 0101 0 0 1 1
0001 0100 0000 0110 0 1 0 0
0000 0001 0001 0110 0 0 1 1
0001 0100 0000 0111 0 1 0 0
0000 0001 0001 0111 0 0 1 1
0001 0100 0000 1000 0 1 0 0
0000 0001 0001 1000 0 0 1 1
0001 0100 0000 1001 0 1 0 0
0000 0001 0001 1001 0 0 1 1
0001 0100 0000 1010 0 1 0 0
0000 0001 0001 1010 0 0 1 1
0001 0100 0000 1011 0 1 0 0
0000 0001 0001 1011 0 0 1 1
0001 0100 0000 1100 0 1 0 0
0000 0001 0001 1100 0 0 1 1
0001 0100 0000 1101 0 1 0 0
0000 0001 0001 1101 0 0 1 1
0001 0100 0000 1110 0 1 0 0
0000 0001 0001 1110 0 0 1 1
0001 0100 0000 1111 0 1 0 0
0000 0001 0001 1111 0 0 1 1
0001 0100 0000 0000 1 0 0 0
0010 0001 1111 0000 1 0 1 1
0011 0001 1111 1111 0 0 1 1
0100 0011 0011 1110 1 1 0 0
0011 0001 1111 1110 1 0 1 1
0100 0011 0011 1101 1 1 0 0
0011 0001 1111 1101 1 0 1 1
0100 0011 0011 1100 1 1 0 0
0011 0001 1111 1100 1 0 1 1
0100 0011 0011 1011 1 1 0 0
0011 0001 1111 1011 1 0 1 1
0100 0011 0011 1010 1 1 0 0
0011 0001 1111 1010 1 0 1 1
0100 0011 0011 1001 1 1 0 0
0011 0001 1111 1001 1 0 1 1
0100 0011 0011 1000 1 1 0 0
このLEDの点灯パターンの表については、ライセンスの対象外とします。
ここまでで、動画の冒頭約7秒分です。
動画は30秒あり、まだまだ続きますが、プログラムカウンタ、4ビットレジスタ、フラグレジスタの全ての状態が最初と同じになったため、これ以降は同じパターンが繰り返されるだけになると推測できるため、書き出しを打ち切りました。
次に、動作を決定するROMのデータを抽出するため、このデータに対し、CyberChef で以下の加工を行いました。
- 4ビットレジスタとフラグレジスタの値を集め、動作を決定するROMのアドレスを抽出する。
同時に、動作を決定するROMが出力しているデータの情報も抽出し、反転するための印を付加する。
(動作を決定するROMが出力しているデータを表すLEDはデータが0のとき点灯するので、反転させてLEDの点灯状態からデータに変換する)
このとき、回路図を参照し、データの上位ビットほど先に来るようにしておく。 - 付加した印を用いてデータを反転させる。
- ソートを行う。(ROMのアドレス順に並ぶ)
- 重複した行を削除する。
以下が実際のRecipeです。
Find / Replace, 4 more - CyberChef
そして、これが解析結果です。
addr data
00010 001
00011 001
00101 110
00110 111
00111 110
01000 110
01001 111
この解析結果、すなわち動作を決定するROMの中身については、ライセンスの対象外とします。
アドレスは、上位4ビットが命令の上位4ビット、下位1ビットがフラグレジスタの値を表しています。
データは、上位ビットから順に、フラグレジスタを更新するか、4ビットレジスタを更新するか、プログラムカウンタを更新するかを表しています。
Foolish4では、レジスタとして 74HC161 が使われており、ROMの出力が 0 のときは更新が行われ、1のときは更新しません。
ついでに、実行しているプログラムについても以下のRecipeで解析してみました。
Find / Replace, Sort, Unique - CyberChef
これが解析結果です。
addr data
0000 0001 0001
0001 0100 0000
0010 0001 1111
0011 0001 1111
0100 0011 0011
0101 0001 0001
0110 0010 0000
この解析結果、すなわち動画で実行しているプログラムについては、ライセンスの対象外とします。
命令セット
上記の解析結果から、命令の上位4ビットおよびフラグレジスタの状態と、各レジスタを更新するかどうかの関係は、以下のようになることがわかりました。
(✓:更新する、-:更新しない、?:不明)
命令の上位4ビット | フラグレジスタ | プログラムカウンタ | 4ビットレジスタ | フラグレジスタ |
---|---|---|---|---|
0000 | 0/1 | ? | ? | ? |
0001 | 0/1 | - | ✓ | ✓ |
0010 | 0 | ? | ? | ? |
0010 | 1 | ✓ | - | - |
0011 | 0 | - | - | - |
0011 | 1 | ✓ | - | - |
0100 | 0 | ✓ | - | - |
0100 | 1 | - | - | - |
0101~1111 | 0/1 | ? | ? | ? |
よって、Foolish4 には少なくとも以下の命令があるらしいことがわかりました。
0001 mmmm
- 無条件で、4ビットレジスタに
mmmm
を加算した結果に基づき、4ビットレジスタとフラグレジスタを更新する。 - 例えば、
ADD mmmm
と表現できそう。
- 無条件で、4ビットレジスタに
0010 mmmm
- フラグレジスタの値が1のときは、
mmmm
が示す場所にジャンプする。 - フラグレジスタの値が0のときの動作は不明。
- フラグレジスタの値が1のときは、
0011 mmmm
- フラグレジスタの値が0のときは、何もせず素通りする。
- フラグレジスタの値が1のときは、
mmmm
が示す場所にジャンプする。 - 例えば、
JC mmmm
(jump if carry) と表現できそう。
0100 mmmm
- フラグレジスタの値が0のときは、
mmmm
が示す場所にジャンプする。 - フラグレジスタの値が1のときは、何もせず素通りする。
- 例えば、
JNC mmmm
(jump if not carry) と表現できそう。
- フラグレジスタの値が0のときは、
プログラミングと動作の様子
解析によって判明した命令のみを用いて、4ビットレジスタの最下位ビットで337拍子を出力するプログラムを作ってみました。
番地 | 機械語 | ラベル | 動作 |
---|---|---|---|
0000 | 0001 1010 | START | ADD 1010 |
0001 | 0001 0001 | THREE1_SOUND | ADD 0001 |
0010 | 0100 0001 | JNC THREE1_SOUND | |
0011 | 0001 1000 | THREE1_WAIT | ADD 1000 |
0100 | 0100 0011 | JNC THREE1_WAIT | |
0101 | 0001 0011 | THREE2_SOUND | ADD 0011 |
0110 | 0100 0101 | JNC THREE2_SOUND | |
0111 | 0001 1000 | THREE2_WAIT | ADD 1000 |
1000 | 0100 0111 | JNC THREE2_WAIT | |
1001 | 0001 0001 | SEVEN_SOUND | ADD 0001 |
1010 | 0100 1001 | JNC SEVEN_SOUND | |
1011 | 0100 0000 | JNC START | |
1100 | 0100 0000 | JNC START | |
1101 | 0011 0000 | JC START |
以下は、このプログラムの機械語をHEX形式で表したものです。
:0E0000001A114118431345184711494040306A
:00000001FF
出力ポート (4ビットレジスタ) の最下位ビットに 2-way Buzzer Board の auto 端子を接続し、約10Hzのクロックで実行してみました。
ライセンス
この記事は、記事中に明示している対象外の部分を除き、CC BY 4.0 ライセンスとします。
投稿者の人気記事
-
mikecat
さんが
2022/12/25
に
編集
をしました。
(メッセージ: 初版)
ログインしてコメントを投稿する