mikecatのアイコン画像
mikecat 2022年12月25日作成
製作品 製作品 閲覧数 1065
mikecat 2022年12月25日作成 製作品 製作品 閲覧数 1065

Foolish4互換機「LiarCPU」

Foolish4互換機「LiarCPU」

自作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 で回路図を作成しました。

LiarCPU 回路図 メイン部分
LiarCPU 回路図 表示部分

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 で以下の加工を行いました。

  1. 4ビットレジスタとフラグレジスタの値を集め、動作を決定するROMのアドレスを抽出する。
    同時に、動作を決定するROMが出力しているデータの情報も抽出し、反転するための印を付加する。
    (動作を決定するROMが出力しているデータを表すLEDはデータが0のとき点灯するので、反転させてLEDの点灯状態からデータに変換する)
    このとき、回路図を参照し、データの上位ビットほど先に来るようにしておく。
  2. 付加した印を用いてデータを反転させる。
  3. ソートを行う。(ROMのアドレス順に並ぶ)
  4. 重複した行を削除する。

以下が実際の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 と表現できそう。
  • 0010 mmmm
    • フラグレジスタの値が1のときは、mmmm が示す場所にジャンプする。
    • フラグレジスタの値が0のときの動作は不明。
  • 0011 mmmm
    • フラグレジスタの値が0のときは、何もせず素通りする。
    • フラグレジスタの値が1のときは、mmmm が示す場所にジャンプする。
    • 例えば、JC mmmm (jump if carry) と表現できそう。
  • 0100 mmmm
    • フラグレジスタの値が0のときは、mmmm が示す場所にジャンプする。
    • フラグレジスタの値が1のときは、何もせず素通りする。
    • 例えば、JNC mmmm (jump if not carry) と表現できそう。

プログラミングと動作の様子

解析によって判明した命令のみを用いて、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 に 編集 をしました。 (メッセージ: 初版)
ログインしてコメントを投稿する