ハードウェアシミュレータその3

いや、まあ、特に何か記事を書くほどのことがあったわけでもないが、とりあえず進捗はあったので書いて置くことにした。
現状のコードはこちら。
https://gist.github.com/mirichi/cbf87b3626389f4e3c66
以下、変更点など。

処理順変更

いままでは入力に更新通知が届いたNANDゲートがそのまま出力を計算して接続先に通知する方式で処理をしていたが、これだとNANDゲートの接続順に依存して微妙な回路が動いたり動かなかったりする(前回の話)ので、更新通知をキューに突っ込んで、通知が出た順に処理するようにした。
キューの処理自体をどこでやるかで悩んだのだが、NANDゲートの処理や更新通知時だとこいつらはキュー処理から大量に呼ばれるのでよろしくなく、入力信号を表現するためのLineクラスを追加して、こいつの変更でキューを処理するようにした。

Lineクラス

今までのシミュレータではユーザ指定の入力データはOutputPinオブジェクトをNANDゲートに繋ぐ形だったが、入力線を明示するためにLineクラスを導入した。キューの処理をするタイミングを作るのにも役に立った。
後述するロジアナの入力を取得するのにも必要である。NANDゲートには外部から入力データを取得する機能が無く、繋がった出力ピンから取ればいいんじゃな〜いって思ってたんだけども、値固定のOutputPinが繋がっていると、入力を差し替えたときにOutputPinオブジェクトごと入れ替えてしまうので、ロジアナのプローブ接続先が無いという困った状態になっていたのだ。

スリーステートバッファとバス

CPU周りの回路にはデータバスやアドレスバスがあって、出力がたくさんバスに繋がっている構成になる。一般的に出力ピン同士が直接繋がるとHとLを出力したときにショートして壊れるのでそのような接続はしないのだが、そうするとOR回路を大量に使うことになってしまう。
対策としては出力をハイインピーダンス(線を切ったような)状態にすることができるスリーステートバッファを間にかまして、バスに繋がった出力はある瞬間に1つだけの出力を有効にするように制御する。これで1本の線を複数の出力で共有できるので、回路は簡単になる。このような構造を実現するために、シミュレータにスリーステートバッファクラスとバスクラスを用意した。
ちなみに実際の回路にはスリーステートバッファというモノは存在するが、バスというモノは存在しない。バスはそういう用途の線をそう呼んでいるだけだ。シミュレータでは接続されたスリーステートバッファのうちのどれが有効になっているかを調べたりするのが面倒だったので、スリーステートバッファはバスオブジェクトにしか接続できないようにして、出力ピンはバスオブジェクトから出ているような構造にしている。ハイインピーダンスじゃないスリーステートバッファの出力がバスオブジェクトの出力ピンから出る、という感じである。
複数のスリーステートバッファが同時に有効になってしまった場合は出力が予測できなくなってバグるが、物理的にそういうものを作ると壊れるわけなので、そんなもんがきちんと動作(意味がわからないが)する必要は無いと考える。

ロジックアナライザ

DXRuby作者なのでこれを使って簡単な簡易ロジックアナライザを作った。
シミュレータのOutputPinオブジェクトにProbeオブジェクトを接続して回路を動作させると、なんとなく画面に信号のチャートが表示される。非常に適当なコードだがそれっぽく動いている。
DeadBeefROM回路を作ってみたときの結果を表示したのが以下。

この回路についてはまたあとで記事にすると思う。