ブレッドボードコンピュータープログラマー
コンポーネントと消耗品
> ![]() |
| × | 1 |
このプロジェクトについて
このプロジェクトとは何ですか?
これは、BenEaterの8ビットブレッドボードコンピュータープロジェクトの拡張機能です。ブレッドボードコンピューターの作成方法と独自のバージョンの作成方法に関するBenのビデオをフォローすることは、非常に楽しいことです。ベンと一緒にフォローしていない場合は、読む前に彼のビデオを視聴することを強くお勧めします。このプロジェクトは、コンテキストがなければほとんど意味がありません。
コンピュータを構築した後、電源を入れるたびにDIPスイッチを介してプログラムを手動で切り替える必要がある場合、プログラムはほとんど使用されないことがすぐに明らかになりました。たった16バイトで何度も何度も切り替えることでさえ、非常に速く古くなります。また、メモリを256バイトに拡張することを計画していましたが、これは、何らかの方法でプログラムをロードおよび保存できる場合にのみ役立ちます。
どうすればいいですか?
- ブレッドボードコンピュータのRAMからプログラムを保存する
- ブレッドボードコンピュータのRAMにプログラムをロードします
- ブレッドボードコンピューターの起動時に、保存されたプログラムを自動実行します
- シリアル接続を介してメモリの内容を検査および変更する
- ブレッドボードコンピュータをアセンブリ言語でプログラムする
- メモリの内容を分解する
- 命令によるシングルステップ命令
- ブレークポイントを設定し、ブレークポイントまで実行します
これらの機能を紹介するビデオがあります。ビデオの品質をお許しください-私は決してプロのビデオコンテンツクリエーターではありません:
どのように機能しますか?
このプロジェクトを作成する際に、いくつかの目標を念頭に置いていました。
- それを構築するために必要なハードウェアを可能な限り少なくする
- ブレッドボードコンピューター自体への変更をできるだけ少なくします
最初は、EEPROMを使用してプログラムを保存し、次に、EEPROMからブレッドボードコンピューターのRAMにプログラムを転送するためのある種のロジックについて説明しました。ただし、転送ロジックを考え出すことは、私が処理できるよりも複雑であることがわかりました(ハードウェアの近くで作業することを本当に楽しんでいますが、私はソフトウェアの人です)。私はたまたまArduinoの大ファンだったので、ある時点で転送ロジックをArduinoに置き換えることを考え始めました。 Arduinoを使用することは不正行為ではないことを確信するのに少し時間がかかりましたが(結局、Arduino UNOでさえブレッドボードコンピューター自体よりもはるかに強力です)、結果に満足しているので、私は平和になりました。
では、Arduinoは何ができる必要がありますか?さて、それはメモリからデータを読み取り、メモリにデータを書き込むことができる必要があります。これを行うための最も簡単で邪魔にならない方法は、メモリモジュールがすでに持っているのと同じインターフェイス(バス信号と制御信号)を使用することです。 ArduinoデジタルI / Oピンは双方向であるため、そのうちの8つをバスに直接接続すると、Arduinoはバスからの読み取りとバスへの書き込みが可能になります。 RAMモジュールにバスからの読み取りまたはバスへの書き込みを行うために必要なのは、それに応じてMI / RI / RO信号を設定することだけです。現在、これらの信号は通常、制御ロジックのEEPROMによって駆動されるため、Arduinoに信号を制御させると、競合が発生したり、短絡が発生したりする可能性があります。ただし、Benが使用するAT28C16 EEPROMには、すべてのデータ出力をhigh-z状態にするチップイネーブル(CE)入力があります。これにより、Arduinoが信号を操作できるようになります。 RAMを読み取るには、Arduinoは次のことを行う必要があります。
- EEPROMのCE信号をハイに設定します(つまり、制御ロジックを無効にします)
- 読み取る最初のアドレスをバスに出力します
- MI信号をハイに設定し、クロックのロー-ハイ遷移を待ちます
- MIをローに設定し、ROをハイに設定して、クロックのロー-ハイ遷移を待ちます
- バスからデータバイトを読み取る
- 16個のアドレスすべてについて手順2から繰り返します
- EEPROMのCE信号をローに設定します(つまり、制御ロジックを再度有効にします)
以上です。 RAMの内容の書き込みは非常に似ており、Arduinoはバスに書き込み、ROではなくRIを高く設定する必要があります。もちろん、解決すべき技術的な問題がいくつかありますが、上記の基本的なメカニズムがこのプロジェクトの中心です。
ブレッドボードコンピュータにどのような変更を加える必要がありますか?
行う必要のある変更は2つあります。
- 制御ロジックEEPROMのデータ出力にプルダウン抵抗を配置します
- EEPROMが無効になっている間、リングカウンタを無効にします(または継続的にリセットします)
プルダウン抵抗
EEPROMが無効になると、制御信号(AO / AI / EO ...など)が接続されている論理ゲートのプルアップ抵抗がそれらの信号をハイにプルアップするため、プルダウン抵抗が必要です。これは、複数のレジスタがバスに書き込み、競合を引き起こすことを意味します。
74LSゲートの入力のプルアップ抵抗は約10kです。したがって、プルダウン抵抗は、電圧を低領域に下げるのに十分なほど小さくする必要があります。ほとんどの信号線には、3.3kオームの抵抗を使用しました。ただし、2つの例外があります。1つは、「SU」信号が8つの排他的論理和ゲートに接続されていることです。つまり、実効プルアップ抵抗は10kOhm / 8 =1.25kOhmです。プルダウン抵抗は、これを低くするために1kより大幅に小さくする必要があります。幸い、SU(減算)信号はバスとの相互作用を制御しないため、無視してプルダウン抵抗を使用する必要はありません。次に、CE(カウンターイネーブル)には1kのプルダウン抵抗が必要でした。値が大きいと、プログラムカウンターがランダムに動作する場合がありました。
ブレッドボードに、すべての青色LEDを保持するプルダウン抵抗を追加するのが最も簡単であることがわかりました。つまり、LEDアノード(EEPROM出力に接続されている)とGNDの間にあります。
<図>
[ここにHLT / MI / RI信号用の抵抗を取り付けることができなかったので、ブレッドボードの他の場所に抵抗を追加しました]
リングカウンターのリセット
もう1つの変更は、リングカウンタをリセットすることです。技術的には、これはプログラムの保存/ロードを可能にするために実際には必要ではありませんが、Arduinoから制御ロジックへの制御のスムーズな転送を可能にします。重要なのは、CEがハイである(つまり、制御ロジックが無効になっている)限り、リングカウンタを0に保つことです。 ArduinoがCEをローに戻す(制御ロジックを有効にする)と、リングカウンターはゼロになり、ブレッドボードコンピューターは次の命令の実行を開始します。
私のビルドでは、1つのEEPROM出力を使用してリングカウンターをリセットしているため、これについては何もする必要はありませんでした。これにより、命令が終了するとすぐにリングカウンタがリセットされ、パフォーマンスが向上します。また、制御ロジックEEPROMが無効になると、リングカウンターが自動的にリセットされます。EEPROMの出力のプルダウン抵抗が信号をローに引き下げ、リングカウンターをリセットします。
ベンの固定5ステップリングカウンターの実装を使用している場合、CEが高い場合、リセット回路を次のように拡張すると、カウンターがリセットされると思います(下の左/右矢印をクリックして、ベンの元の回路と拡張バージョン):
<図>

ご覧のとおり、さらに3つのNANDゲート、つまりもう1つの74LS00チップが必要です。このアプローチはテストしていませんが、私が見る限りは機能するはずです。
この変更は絶対に必要というわけではありません。最初は省略できます。ロードと保存、およびモニター/アセンブラー/逆アセンブラーは引き続き正常に機能します。ただし、Arduinoから制御ロジックへの制御の移行を必要とするアクションは機能しません。最も注目すべきは、起動時に保存されたプログラムを自動実行することと、デバッガーでシングルステップ実行することです。
Arduinoを設定するにはどうすればよいですか?
スケッチをGITアーカイブからArduinoにアップロードし、次のようにArduinoをブレッドボードコンピューターに接続します。
- Arduino 5Vピン(ない Vin!)ブレッドボードの5Vレールへ
- ブレッドボードのArduinoGNDピンからGNDピンへ
- Arduinoデジタルピン2〜9からバス0〜7
- ROを制御するロジックEEPROM出力を制御するArduinoデジタルピン10
- RIを制御するロジックEEPROM出力を制御するArduinoデジタルピン11
- MIを制御するロジックEEPROM出力を制御するArduinoデジタルピン12
- Arduinoアナログピン0からCLOCK信号
- すべてのArduinoアナログピン3からCE(ピン18) 制御ロジックEEPROM および + 5vへの10k抵抗を介して
それとは別に、回路図に示すように、Arduinoのアナログ1およびアナログ2入力ピンをDIPスイッチおよびプッシュボタンに配線する必要があります(詳細については、添付のフリッツファイルを参照してください)。
<図>

最小限の(ただし機能している)バージョンの場合、次の操作を実行できます。
- AO、CO、EO、IO、ROを制御するEEPROM出力ピンに3.3kオームのプルダウン抵抗を追加します
- 上記の「リングカウンタのリセット」の手順をスキップしてください
- 上記のようにArduinoからブレッドボードへのコンピューター配線を行います(必要に応じて、最後のステップで10k抵抗を省略できます)
- アナログピン1とアナログピン2の両方をGNDに接続します
ロード/保存ボタンを使用するには、回路図に従って、ボタン、DIPスイッチ、および関連する抵抗をアナログピン1および2に接続するだけです。
自動起動機能を使用するには、Arduinoは、リセット中およびプログラムの転送中、プログラムカウンターを0に、リングカウンターを0に保つ必要があります。アナログピン3と+ 5Vの間のプルアップ抵抗は、Arduinoがリセットされている間、制御ロジックを無効に保ちます(したがって、プログラムカウンターは0になります)。リングカウンターについては、上記の「リングカウンターのリセット」の手順に従ってください。
プログラムをロードして保存するにはどうすればよいですか?
上記の最小限のセットアップでは、シリアルインターフェースを介してArduinoを制御できます。 Arduinoと通信するには、PuttyやTeraTermなどのターミナルプログラムが必要です。 Arduinoソフトウェアのシリアルモニターも機能しますが、シリアルモニターの入力領域と出力領域が分離されているため、このシナリオでは少し不格好になります。
- ブレッドボードコンピュータの電源を入れます
- USBケーブルを介してPCをArduinoに接続します
- ターミナルプログラムを起動し、9600ボー、8ビット、パリティなし、1ストップビットに設定します
- ターミナルウィンドウでESCを押して、モニターモードに入ります
- 「。」が表示されます。コマンドプロンプトとして
- 「h」と入力してEnterキーを押すと、サポートされているコマンドのリストが表示されます
最小限の設定で、「m」、「M」、「C」、「l」、および「s」コマンドを使用できるはずです。これらを使用すると、メモリの内容を確認したり、メモリの内容を変更したり、プログラムをロードおよび保存したりできます。
ボタンを使用してプログラムを保存またはロードするには:
- ブレッドボードコンピューターの時計をオフにします
- データを保存するファイル番号を選択するようにDIPスイッチを設定します。
- 「保存」または「ロード」ボタンを押します。 CEに接続されたLEDが点灯し、Arduinoが制御を取得したことを示します
- ブレッドボードコンピューターの時計をオンにします。 Arduinoがアドレスを循環しているのが見えるはずです(メモリアドレスレジスタのLEDを見てください)
- バスのLEDの点滅が止まり、メモリアドレスレジスタのLEDが表示されて1111が表示されるまで待ちます
- ブレッドボードコンピューターの時計をオフにします。 CEに接続されたLEDが消灯し、制御が制御ロジックに戻ったことを示します
起動時にプログラムを自動的に実行するには(必要な回路がすべて揃っていることを確認してください)、DIPスイッチをプログラムが保存されているファイル番号に設定し、ブレッドボードコンピューターの電源を入れます(またはリセットボタンを押します)。 2つの特殊なケースがあります。すべてのDIPスイッチがオフの場合、コンピューターは自動起動せずに定期的に起動します。すべてのDIPスイッチがオンになっている場合、Arduinoは起動時に直接モニターモードになります。
アセンブラと逆アセンブラはどのように使用しますか?
アセンブラー/逆アセンブラーおよびデバッガー機能を使用するには、最初に、特定のセットアップに準拠するようにArduinoのプログラムを変更する必要があります。 opcodes_4bit構造を定義するソースコードのセクションを見つけます:
struct opcodes_struct opcodes_4bit [] ={{"NOP"、B00000000、0、false}、{"LDA"、B00010000、2、true}、... {".OR"、B11111110、0、 true}、//開始アドレスを設定{".BY"、B11111111、0、true}、// 1バイトのデータを定義{NULL、0、0、false}};
各行は1つのオペコードを指定します:
- 最初のフィールドはニーモニック(「LDA」)です。すぐにアドレス指定するには、ニーモニックに「#」を含めます。したがって、ベンが「LDI」と呼ぶものは、ここでは「LDA#」と呼ばれます。私がC64で6510アセンブラをプログラミングして育ったと言えますか?
- 2番目のフィールドはオペコード自体です。下位4ビットは常に0である必要があります(特別なオペコード.ORおよび.BYを除く、以下を参照)
- 3番目のフィールドは、オペコードの実行にかかるサイクル数です(これはフェッチサイクルに追加されます)。たとえば、私の実装では、LDAにはオペコード0001があり、実行には合計4サイクルかかり、そのうち2サイクルはフェッチサイクルです。ベンの指示に従った場合(すべてのオペコードが5サイクルを使用する場合)、これは常に3である必要があります。
- 最後のフィールドは、このオペコードに引数が必要かどうかを指定します。たとえば、LDAには引数が必要ですが、OUTには必要ありません。
ブレッドボードコンピュータに実装したオペコードを反映するように、このリストを調整する必要があります。最後の2行は、アセンブラによって使用される特別なオペコードであり、そのままにしておく必要があります。
すべてのオペコードを入力したら、ソフトウェアをArduinoにアップロードします。ターミナルを接続し、モニターモードに入ります(ターミナルウィンドウでESCを押すか、すべてのDIPスイッチをオンに設定します)。これで、プログラムを逆アセンブルできるようになります。モニターに「d」と入力すると、アドレス0で逆アセンブルが開始されます。
アセンブラは最小限ですが、かなりうまく機能します。 「a」と入力して、アドレス0で組み立てを開始します。規則は次のとおりです。
- 行が空白で始まらない場合は、ラベル定義で始まらなければなりません。ラベルは、英字で始まり、その後に英数字が続く必要があります。長さは最大3文字で、大文字と小文字が区別されます。
- 行の最初の空白の後、アセンブラはニーモニック(LDA、STA、OUT ...)を期待します。
- 特別なニーモニック「.BY」は、現在の場所に格納されるデータバイトを直接指定します。
- 特別なニーモニック「.OR」は、アセンブラに新しいアドレスでのアセンブルを続行するように指示します。
- 引数が英字で始まる場合、それはラベルであると見なされます。
- 数値引数はすべて10進数である必要があります。 16進数を指定するには、引数の前に「$」を付けます。たとえば、16進数のFF番号をAレジスタにロードするには、「LDA#$ FF」を使用します。
- 「;」の後のすべてコメントと見なされ、無視されます。
たとえば、フィボナッチコードは次のように入力できます。
rst LDA#0; x =0 STA x LDA#1; y =1 STA y lp ADD x; z =y + x STA z JC rst;オーバーフローした場合は再起動しますOUT;印刷zLDA y; x =y STA x LDA z; y =z STA y JMP lp;ループx.BY 0 y .BY 0 z .BY 0
制限は何ですか?
ArduinoのRAMスペースを節約するために、アセンブラは1パスアセンブラとして機能します(そうでない場合、Arduinoはすべてのソースコードをバッファリングする必要があります)。アセンブラは、入力時にオペコードをブレッドボードコンピュータのメモリに書き込みます。これは、ブレッドボードコンピュータのクロック速度によって組み立てが遅くなることを意味します。テキストをコピーしてターミナルウィンドウに貼り付けると、Arduinoが9600ボーで入ってくる文字に追いつくことができないため、文字が失われる可能性があります(ブレッドボードコンピューターの時計を待つのに時間がかかりすぎるため)。これを回避するには、ボーレートを下げるか、送信される文字間の遅延を指定する設定を提供するTeraTermを使用します。もう1つの回避策は、ブレッドボードコンピューターのクロック速度を上げることです。私の時計は160kHzまで上がり、その速度でコードを9600ボーで問題なくコピーアンドペーストできます。
デフォルトの構成では、Arduinoスケッチはブレッドボードコンピューターで最大約1〜2kHz(多分もう少し)のクロック周波数を処理できます。デフォルト構成のベンの時計は500Hzより速くならないことに注意してください。時計が速い場合は、 #ifdef FAST_IO
を探してください コードを切り替えます。 FAST_IOをオンにすると、Arduinoは最大250kHzのクロック速度で動作するはずです。 160kHzまでテストしました。タイムクリティカルなループをアセンブラに直接実装することで、おそらくより高速をサポートすることは可能ですが、正直なところ、160kHzのクロック速度は、機能が制限されているブレッドボードコンピュータではすでに速すぎると感じています。 FAST_IOをオンにする前に、コード内の対応するコメントを必ずお読みください。
Arduinoには1kのEEPROMがあるため、1024/16 =64の異なるプログラムを保持できます。構成データを保存するために16バイトが予約されているため、実際には63です。それは多くはありませんが、おそらくあなたが思いつくことができるすべてのプログラムを保持するのに十分です。それらのプログラム番号0〜15のみがディップスイッチ(自動起動の場合は1〜14)を介して選択できますが、「s」および「l」コマンドは0〜62の全範囲で機能します。
少し面倒に見えます。あなたはそれを整えることができますか?
はい!ここでの私の最終バージョンでは、Arduino UNOの代わりに、実際には裸のAtmega 328Pチップ(16MHzのクリスタルとコンデンサーと一緒に)を使用しました。 Arduinoはとにかく電源から5Vを直接使用しているため、UNOの電圧レギュレータはここでは必要ありません。唯一の損失は、Atmegaと通信するために別のUSB-シリアルコンバーター(FTDIまたは同様のもの)を使用する必要があることです。その全体的な外観は、ブレッドボードコンピューターの他の部分とはるかによく適合します:
<図>

もう1つの最適化は、Arduino / Atmegaからブートローダーを削除することです。これにより、Arduinoブートローダーの起動中の2秒の遅延がなくなります。興味のある方はその方法を投稿します。コメントで教えてください!
コード
ブレッドボードコンピュータープログラマー
ブレッドボードコンピュータープログラマー向けのArduinoスケッチhttps://github.com/dhansel/programmer 回路図
drive_pD8k28E85v.fzz 製造プロセス