RETROF-16 部品実装（その５）

#RETROF-16 部品実装（その５）

⇒ [本記事に対応する最新回路図はこちらです](https://qiita.com/RETROF/items/08aec337163b24c49cff)
⇒ [RETROF-16 部品実装（その１）はこちらです](https://qiita.com/RETROF/items/dc597ef541080021b495)
⇒ [RETROF-16 部品実装（その２）はこちらです](https://qiita.com/RETROF/items/93dcc2996150c8328ac5)
⇒ [RETROF-16 部品実装（その３）はこちらです](https://qiita.com/RETROF/items/7e23c14dd9b824b1c847)
⇒ [RETROF-16 部品実装（その４）はこちらです](https://qiita.com/RETROF/items/6f42bc912d37f5e308d0)

この時点での基板の外観は以下となります。基板の左上部分に部品の未実装が見られますが、これはCPU本体を構成する部品群ではなく、画像を表示する回路を構成する部品群であり、実装を「後回し」にしているためです。

![R0010259 - コピー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535901/be9a3d37-5619-5560-16bc-107e668b2dca.jpeg)


---

#WindowsPCとのUSB接続
前回までは主に「部品の実装」と「機械語命令の動作試験」を同時に行う手法を紹介してきました。しかし「演算」や「転送」のような単純命令は「1命令ずつ手作業でプログラミングし結果を確認する」で十分でしたが、比較的大きな動作検証用のプログラムを基板上のスイッチの操作でプログラミングするのは（不可能ではありませんが）現実的ではありません。
また仮に「何百行。何千行ものプログラム」を基板上のスイッチ操作でプログラミングできたとしても、それをセーブする手段がありませんので電源を切ると全てが消えてしまいます。これでは非常に試験効率が悪いのは明らかです。
そこで本機**RETROF-16**にWindowsPCとUSB接続を確立し、WindowsPCから本機のプログラムを書替えや、実行を行う機能を搭載しました。今回は「部品実装」ではなく、その機能の紹介が主になります。

なお、本機**RETROF-16**から見た場合、**WindowsPC**は単なる「補助装置」に過ぎません。しかし自作CPUを他の市販マシンと接続した状態のままデモを行うと、多くの方が『このデモは**WindowsPC**上で処理している』と誤解します。本機はこの様な誤解を防ぐため、**WindowsPC**から完全に切り離した状態でも、基板上のスイッチ操作でOSやコンパイラ（あるいは各種アプリの起動）が可能になる様に配慮して作りました。

#画面イメージ

![WS2019_000783.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535901/07c2d8a1-a388-28d8-e056-deafdee62963.jpeg)


#コマンド一覧

*xxxx*は任意の16進値。但し0xFFFFを超える場合は下位16bitのみが有効。
表中の略語の意味は以下の通り

* Acc : アキュムレータ
* PC : プログラムカウンタ
* WR : ワーキングレジスタ
* MEM : WRがアドレスを指定する主メモリ
* IN : 基板上のトグルスイッチ


|コマンド|意味|動作|
|:------:|:-:|---|
|o|open|Windowsパソコンとの回線を開く
|c|close|Windowsパソコンとの回線を閉じる
|i|increment|WRの値を+1する
|d|decrement|WRの値を-1する
|w *xxxx*|write|MEMの値を*xxxx*に書替える。
|e *xxxx*|execute|指定アドレスから実行を開始する<br>（引数省略時は0x8000）
|ep|execute<br>from PC|PCが示すアドレスから実行を開始する<br>（引数省略時は0x8000）
|ez|execute<br>from zero|0番地から実行を開始する
|h|halt|実行状態（もしくはHALT命令による停止状態）<br>の場合は実行待機状態へ移行する
|||
|pm|present MEM|MEMの値を基板上の下段LEDに表示する
|pp|present PC|PCの値を基板上の下段LEDに表示する
|pa|present Acc|Accの値を基板上の下段LEDに表示する
|pi|present IN|INの値を基板上の下段LEDに表示する
|||
|rm|read MEM|MEMの値を対話エリアに表示する
|rp|read PC|PCの値を対話エリアに表示する
|ra|read Acc|AccMの値を対話エリアに表示する
|rf|read ft245|ft245の値を対話エリアに表示する
|||
|sw *xxxx*|set W|Wの値を*xxxx*に書き替える<br>（引数省略時は0x8000）
|sp *xxxx*|set PC|PCの値を*xxxx*に書き替える<br>（引数省略時は0x8000）
|sa *xxxx*|set Acc|Accの値を*xxxx*に書き替える
|sf *xx*|set ft245|ft245に直接8bit値を書込む。<br>引数は下位8bitのみ有効
|||
|clear|clear|対話エリアの文字列を消去する<br>(これは基板に対するコマンドではない)





#対応する回路

対応する回路を以下に示します。（ [全体回路図はこちらを参照願います](https://qiita.com/RETROF/items/08aec337163b24c49cff)）
FT245の説明と、それを制御するWindows側のアプリの作り方は以下を参照願います。

・[RETROF-16統合開発環境の製作（１）DockingWindowの実現とRichTextBoxの継承](https://qiita.com/RETROF/items/66a9694acc4dd625d003)
・[RETROF-16統合開発環境の製作（２）FT245をC#から制御する](https://qiita.com/RETROF/items/13e0096ee2e5415f6878)






![WS2019_000762.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535901/b13bb4f4-b565-ee2a-65f4-f206ecfe2e58.jpeg)

基本的にはFT245の出力を、本機RETROF-16の各種制御信号線にOR接続（正確には負論理のAND接続）しているだけです。74LS541は本機能を用いない時にFT245の出力を全体回路から切り離すために設けています。なお、74LS541の9番ピンと11番ピンは実際には接続がありますが、ここでは無接続としています。
なお、制御すべき信号線が11本あるのに対し、FT245の出力は7本しか使えない（8bitのうち1bitはアキュムレータの状態監視入力に使っている）ので、FT245のbit1とbit2出力は複数の信号線の制御を兼務しています。

FT245の各ビットが制御する信号線は以下の通りになります。FT245のbit1とbit2出力は複数の信号線の制御を兼務しているので、下記の表でもbit1とbit2は複数回登場します。

|bit|回路図上の<br>信号線名|役割|
|:-:|:-:|---|
|0|**ASO**<br>（入力）|<br>アキュムレータの最下位ビット（MSB）の値を監視する
|2<br><br>1<br>|<br>**D_0**<br><br>**D_1**<br><br>|データバスに何を出力するのかを決める<br>**bit2=0&nbsp;&nbsp;bit1=0***（Y0）*→ トグルスイッチの値を出力<br>**bit2=0&nbsp;&nbsp;bit1=1***（Y2）*→ **Acc**の値を出力<br>**bit2=1&nbsp;&nbsp;bit1=0***（Y1）*→ **PC**の値を出力<br>**bit2=1&nbsp;&nbsp;bit1=1***（Y3）*→ 主メモリの値を出力<br><br>　**bit2**と**bit1**は「U32B 74LS139」の入力**A,B**に対応。<br>&nbsp; *（Yn）*は74LS139のアクティブになる出力ピン

ビット2,1は他にも様々な役割を兼務します


|bit|回路図上の<br>信号線名|役割|
|:-:|:-:|---|
|1|出力|プログラム実行中にこのビットを**0**にすると強制停止する
|1|出力|プログラム停止中に**Acc**を右シフトすると<br>このビットの値が**Acc**のMSBに設定される
|2|出力|この値を**0**にして、ビット6に立下りパルスを与えると<br>**Acc**か入力ポートのどちらかの値（ビット0で決まる）が<br>**WR**に書き込まれる
|2|出力|この値を**0**にして、ビット3に立下りパルスを与えると<br>**Acc**が右シフトする

ビット3～7の役割は以下の通り


|bit|回路図上の<br>信号線名|役割|
|:-:|:-:|---|
|3|出力|**Acc**にクロックを与える
|4|出力|データバスの値を**PC**にセット（**0**で非同期ロード）
|5|出力|データバスの値をメモリへ書込（**0**で非同期書込）
|6|出力|**WR**にクロックを与える
|7|出力|プログラムの実行を開始する<br>ステップ実行時はこの信号がクロックとなる

#変更のあったソースコード

本ツールのソースコードは以下の（２）の一部書き替えになります。

・[RETROF-16統合開発環境の製作（１）DockingWindowの実現とRichTextBoxの継承](https://qiita.com/RETROF/items/66a9694acc4dd625d003)
・[RETROF-16統合開発環境の製作（２）FT245をC#から制御する](https://qiita.com/RETROF/items/13e0096ee2e5415f6878)

書替えが必要なソースファイルは下記の3つです。

* FT245.cs
* MainFormEx.cs
* ShellTextBox.cs


<details><summary>**FT245.csのソースコード**<br>黒三角印のクリックでFT245.csのソースコードを参照できます</summary><div>

```cs

using System;
using System.Runtime.InteropServices;  //DLLのインポートに必要
//#####################################################################################################################
///     RETROF-16(令和版改) 統合開発環境　FT245RLインタフェイスクラス  2020-11-26  SID.Gataro   
//#####################################################################################################################
namespace RETROF {
    class RETROF245 : FT245RL {
        private const int BUFFERSIZE = 2_500_000;       // 65536*36 = 2_400_000 送信はバッファにため一気に行なう
        protected byte[] Buffer = new byte[BUFFERSIZE]; // ブロック転送用バッファ
        protected uint Buffer_ptr;                      // バッファポインタ
        private const int MAXRATE = 0x1F6_EE0;          ///要注意　2020/4/24         
        //　レートの最大値はFTDI社のマニュアルを見ても良く判らない。BitBangモードで2.4Mbit/sでの転送が限界
        //  0x1F6EE0 (1,260,000)より大きな値を指定するとレート設定エラーとなる
        //  但し、レート指定値が500,000前後辺りで、それ以上の値を設定しても転送速度は変わらなくなる
        private const int IO_MODE = 0xFE;                // D7-D1は出力、D0のみ入力
        //-------------------------------------------------------------------------------------------------------
        /// Open :　bitのI/Oの割当（ソースコード内で固定としている）、転送速度をRETROF-16用の値に設定してオープン　
        //-------------------------------------------------------------------------------------------------------
        public uint Open() {
            Buffer_ptr = 0;
            Buffer[Buffer_ptr] = 0xFF;
            uint sts;
            if ((sts = Open(0)) != 0) goto err;
            if ((sts = SetBaudRate(MAXRATE)) != 0) goto err;
            //一瞬「L」信号がでるのを防ぐテク　2015-2-10
            if ((sts = SetBitMode(0x00)) != 0) goto err;   //全bitを入力にして 
            Buffer[0] = 0xFF;
            if ((sts = Write(Buffer, 1)) != 0) goto err;   //FFを出力してから
            if ((sts = SetBitMode(IO_MODE)) != 0) goto err;   //改めて全ビットを設定
            return (0);
        err:
            Close(); //このクローズは２重オープンエラー時以外は意味はない。
            return sts;
        }

        //-------------------------------------------------------------------------------------------------------
        /// Close
        //-------------------------------------------------------------------------------------------------------
        new public uint Close() { return base.Close(); }

        //-------------------------------------------------------------------------------------------------------
        /// Put : バッファに1バイト書込み。　バッファサイズを超えない事は呼び出し側の責任、万一超えた場合は例外を投げる
      　//-------------------------------------------------------------------------------------------------------
        public void Put(int c) {
            //バッファサイズを超えない事は、呼び出し側の責任、万一超えた場合は、NotImplementedExceptionを投げる
            if (Buffer_ptr >= BUFFERSIZE) throw new NotImplementedException();
            Buffer[Buffer_ptr++] = (byte)(c & 0xff);
            return;
        }

        //-------------------------------------------------------------------------------------------------------
        /// Flush : 今までPutしたデータを一気に送る（1byteずつ送ると、bigbangモードの効果が激減。速度が全然でません）
        //-------------------------------------------------------------------------------------------------------
        public uint Flush() {
            uint sts = Write(Buffer, Buffer_ptr);
            Buffer_ptr = 0;
            return (sts);
        }
        //引数に「関数」がある必要な場合は、先にそれを実行する
        public uint Flush(Action f) {
            f();
            uint sts = Write(Buffer, Buffer_ptr);
            Buffer_ptr = 0;
            return (sts);
        }
        //引数に「引数がある関数」がある必要な場合は、先にそれを実行する
        public uint Flush(int n, Action<int> f) {
            f(n);
            uint sts = Write(Buffer, Buffer_ptr);
            Buffer_ptr = 0;
            return (sts);
        }

        //-------------------------------------------------------------------------------------------------------
        /// Get : デバイスから1バイト直接読み込み　
        //-------------------------------------------------------------------------------------------------------
        public uint Get(ref int read_data) {
            return GetBitMode(ref read_data);
        }

        //-------------------------------------------------------------------------------------------------------
        /// 以下は RETROF-16 の「JTAGもどき」専用　
        //-------------------------------------------------------------------------------------------------------
        /// RETROF-16をFT245RLで制御する際の指令コード定義
        const ushort RM_NON        = 0b__1111_1111;
        const ushort RM_INPUT_MASK = 0b__0000_0001; // bit0

        const ushort RM_SHOW_SW    = 0b__1111_1001; // bit2=0 bit1=0 
        const ushort RM_SHOW_ACC   = 0b__1111_1011; // bit2=0 bit1=1 
        const ushort RM_SHOW_PC    = 0b__1111_1101; // bit2=1 bit1=0 
        const ushort RM_SHOW_MEM   = 0b__1111_1111; // bit2=1 bit1=1    synonym RM_NON

        const ushort RM_HALT       = 0b__1111_1101; // synonym RM_SHOW_PC
        const ushort RM_SHIFT0     = 0b__1111_1001; // synonym RM_SHOW_SW
        const ushort RM_SHIFT1     = 0b__1111_1011; // synonym RM_SHOW_ACC
        const ushort RM_LOAD_WR    = 0b__1111_1011; // synonym RM_SHOW_ACC , RM_SHIFT1

        const ushort RM_ACK        = 0b__1111_0111; // bit3
        const ushort RM_PLD        = 0b__1110_1111; // bit4
        const ushort RM_MWR        = 0b__1101_1111; // bit5
        const ushort RM_WCK        = 0b__1011_1111; // bit6
        const ushort RM_RUN        = 0b__0111_1111; // bit7

        //-------------------------------------------------------------------------------------------------------
        /// データバスに放出するデータを選択する関数群　
        //-------------------------------------------------------------------------------------------------------
        public uint DisplayMemy() /*主メモリの値を放出する*/           { Put(RM_SHOW_MEM); return (Flush()); }
        public uint DisplayAcc()  /*アキュムレータの値を放出する*/      { Put(RM_SHOW_ACC); return (Flush()); }
        public uint DisplayPC()   /*プログラムカウンタの値を放出する*/  { Put(RM_SHOW_PC); return (Flush()); }
        public uint DisplaySW()   /*ポート（トグルSW）の値を放出する*/　{ Put(RM_SHOW_SW); return (Flush()); }

        //-------------------------------------------------------------------------------------------------------
        /// アキュムレータ操作関数群　
        //-------------------------------------------------------------------------------------------------------
        ///　アキュムレータを右シフトする。引数が0なら最上位には0が、引数が0以外なら最上位には1が入る。フラッシュはしない
        public void ShiftAcc(int MSB) {  //左シフトはできないので「Shift」のみで「右シフト」であることが判る
            int shift_mask = (MSB==0) ? RM_SHIFT0 : RM_SHIFT1;
            Put(shift_mask); Put(shift_mask & RM_ACK); Put(shift_mask); Put(RM_SHOW_ACC);
        }
        ///　アキュムレータに任意値をセットする。任意値は引数の下位16ビットで指定する。フラッシュはしない
        public void SetAcc(int n) { for (int i = 0; i < 16; ++i, n >>= 1) ShiftAcc(n & 1); }
        ///　アキュムレータの値を読み込む。非破壊読み出し。フラッシュする
        public uint ReadAcc(ref int return_value) {
            uint sts = 0 ;
            int read_data = 0;
            for (int i = 0; sts == 0 && i < 16; ++i) {
                Get(ref read_data);
                return_value |= ((read_data & RM_INPUT_MASK) << 16); // LSBを把握し、return_value の16bit目にセット
                return_value >>= 1;                                  // return_valueを右シフト
                ShiftAcc(read_data & RM_INPUT_MASK);　　　　　 　　　 // Acc自体を右シフト、MSBにはシフト前のLSBをセット 
                sts = Flush();
            }
            return (sts);
        }

        //-------------------------------------------------------------------------------------------------------
        ///　ワークレジスタ（WR）操作関数群  （WRはここでは主メモリのアドレス指定レジスタとして機能する)　
        //-------------------------------------------------------------------------------------------------------
        /// WRに任意値（省略時は0x8000）をセットする。ACCも同じ値にる。フラッシュはしない。データバスはメモリ値を放出。
        public void SetWR(int n) { SetAcc(n); Put(RM_LOAD_WR & RM_WCK); Put(RM_LOAD_WR); }
        public void SetWR() { SetAcc(0x8000); Put(RM_LOAD_WR & RM_WCK); Put(RM_LOAD_WR); }
        /// WRをインクリメントする。フラッシュはしない。データバスはメモリ値を放出。
        public void IncWR() { Put(RM_SHOW_MEM & RM_WCK); Put(RM_SHOW_MEM); }
        /// WRをデクリメントする。フラッシュはしない。データバスはメモリ値を放出。
        public void DecWR() { Put(RM_HALT); Put(RM_WCK & RM_HALT); Put(RM_SHOW_MEM); }

        //-------------------------------------------------------------------------------------------------------
        ///　主メモリ（MEM）操作関数群　
        //-------------------------------------------------------------------------------------------------------
        /// MEM（アドレスはWR）に任意値を書込む。ACCも同じ値になる。ROM領域の場合は何もしない。フラッシュはしない。　
        public void WriteMem(int n) { SetAcc(n); Put(RM_SHOW_ACC); Put(RM_SHOW_ACC & RM_MWR); Put(RM_SHOW_MEM); }
        /// MEM（アドレスはWR）の値を読む。ACCも同じ値になる。フラッシュする。　
        public uint ReadMem(ref int n) { Put(RM_SHOW_MEM); Put(RM_SHOW_MEM & RM_ACK); return (ReadAcc(ref n)); }
        
        //-------------------------------------------------------------------------------------------------------
        ///　プログラムカウンタ（PC）操作関数群　
        //-------------------------------------------------------------------------------------------------------
        /// PCに任意値を書込む。ACCも同じ値になる。ROM領域の場合は何もしない。フラッシュはしない。　
        public void SetPC(int n) { SetAcc(n); Put(RM_SHOW_ACC); Put(RM_SHOW_ACC & RM_PLD); Put(RM_SHOW_PC); }
        /// PCの値を読む。ACCも同じ値になる。フラッシュする。　
        public uint ReadPC(ref int n) { Put(RM_SHOW_PC); Put(RM_SHOW_PC & RM_ACK); return (ReadAcc(ref n)); }

        //-------------------------------------------------------------------------------------------------------
        ///　ポート（IN）操作関数群　
        //-------------------------------------------------------------------------------------------------------
        /// ポートの値を読む。ACCも同じ値になる。フラッシュする。　
        // この関数は実装できない。データバスにポートの値を放出している状態は、Accはシフトのみでロードはできないから

        //-------------------------------------------------------------------------------------------------------
        ///　実行/停止操作関係の関数群　
        //-------------------------------------------------------------------------------------------------------
        ///指定アドレス（省略時は 0x0800） から実行する。既に実行中でもエラーとしていない点に留意。フラッシュはしない。
        public void Execute(int adrs) { SetPC(adrs); /* RUN信号は一瞬  Put(RM_NON); */Put(RM_RUN); Put(RM_NON); }
        public void Execute() { SetPC(0x8000); /* RUN信号は一瞬  Put(RM_NON); */Put(RM_RUN); Put(RM_NON); }
        ///現在のPCが示すアドレスから処理を再開する。既に実行中でもエラーとしていない点に留意。フラッシュはしない。
        public void Continue() {   /* RUN信号は一瞬 */ /*Put(RM_NON);*/ Put(RM_RUN); Put(RM_NON); }
        ///強制停止する 既に停止中でもエラーとしていない点に留意。フラッシュはしない。
        public void Suspension() { /* 停止信号は一瞬 */ /*Put(RM_NON); */Put(RM_HALT); Put(RM_NON); }
    }

    class FT245RL {
        //-------------------------------------------------------------------------------------------------------
        // ft2xx.dllのインポート （VSのdebugフォルダとReleaseフォルダにftd2xx.dllを要す）
        //-------------------------------------------------------------------------------------------------------
        //binの下の.exeと同じフォルダか Windows\SysWOW64　にFTD2xx.dllを置く事
        [DllImport("ftd2xx.dll")]
        private static extern uint FT_Open(Int16 DeviceNumber, ref uint Handle);
        [DllImport("ftd2xx.dll")]
        private static extern uint FT_Close(uint Handle);
        [DllImport("ftd2xx.dll")]
        private static extern uint FT_SetBaudRate(uint Handle, int BaudRate);
        [DllImport("ftd2xx.dll")]
        private static extern uint FT_SetBitMode(uint Handle, byte Mask, byte Mode);
        [DllImport("ftd2xx.dll")]
        private static extern uint FT_Write(uint Handle, byte[] buffer, uint req_size, ref uint written_size);
        [DllImport("ftd2xx.dll")]
        private static extern uint FT_GetBitMode(uint Handle, ref int read_data);

        //-------------------------------------------------------------------------------------------------------
        // FT245RLクラス　プロパティ
        //-------------------------------------------------------------------------------------------------------
        private byte BIGBANGMODE = 1;
        private uint Handle;         // FTDIチップ識別子 　
        private uint Written_size;   // 書き込んだサイズ

        //-------------------------------------------------------------------------------------------------------
        // コンストラクタ・デストラクタ
        //-------------------------------------------------------------------------------------------------------
        public FT245RL() { }
        ~FT245RL() { FT_Close(Handle); }  //クローズ忘れに対する保証。通常は2重クローズになるが問題はない

        //-------------------------------------------------------------------------------------------------------
        // オリジナル関数　
        //-------------------------------------------------------------------------------------------------------
        protected uint Open(short device) { return FT_Open(device, ref Handle); }
        protected uint Close() { return FT_Close(Handle); }
        protected uint SetBaudRate(int rate) { return FT_SetBaudRate(Handle, rate); }
        protected uint SetBitMode(byte mask) { return FT_SetBitMode(Handle, mask, BIGBANGMODE); }
        protected uint Write(byte[] buffer, uint size) { return FT_Write(Handle, buffer, size, ref Written_size); }
        protected uint GetBitMode(ref int read_data) { return FT_GetBitMode(Handle, ref read_data); }
    }
}


```
</div></details>




<details><summary>**MainFormEx.csのソースコード**<br>黒三角印のクリックでMainFormEx.csのソースコードを参照できます</summary><div>

```cs

using System;
using System.Windows.Forms;
using System.Text.RegularExpressions; ///正規表現の比較に必要


namespace RETROF {
    public partial class MainForm : Form {
        RETROF245 FT245 = new RETROF245();
        //--------------------------------------------------------------------------------------------------------------
        ///コンソールウィンドウがEnterキーを押下した時に発生する独自イベントの処理
        //--------------------------------------------------------------------------------------------------------------
        private ShellTextBox ShellPtr; //シェルにアクセスするためのポインタの保存領域
        void OnShell(object sender, ShellEventArgs e) {
            ShellPtr = (ShellTextBox)sender;
            e.Result = "!Unknown command"; //仮設定、再設定しないとこれが表示される
                                           //文字列先頭の！は特別な色（一般には赤）で表示する指定文字
                                           //コマンドの前後の空白を取る
            string command = e.Command.Trim();
            //コマンドと引数に分割
            string[] com_arg =  Regex.Split(e.Command.Trim(),@"\s+");
            //トークン数が0なら何もしないでリターン
            if (com_arg.Length == 1 && com_arg[0] == "") { e.Result = ""; return; }
            //トークン数が3以上なら「引数多すぎ」
            if (com_arg.Length > 3) { e.Result = "!Too many argument."; return; }
            //「命令文字列のみ」に一致するか？
            if (Regex.IsMatch(command, @"^\w+$")) { e.Result = Do(com_arg[0], -1, -1); return; }
            //「命令文字列　16進引数」に一致するか？
            if (Regex.IsMatch(command, @"^\w+\s+[a-fA-F0-9]{1,4}$")) {
                e.Result = Do(com_arg[0], (int)System.Convert.ToUInt32(com_arg[1], 16), -1);
                return;
            }
            //「命令文字列　16進引数　16進引数」に一致するか？
            if (Regex.IsMatch(command, @"^\w+\s+[a-fA-F0-9]{1,4}\s+[a-fA-F0-9]{1,4}$")) {
                e.Result = Do(com_arg[0], (int)Convert.ToUInt32(com_arg[1], 16), (int)Convert.ToUInt32(com_arg[2], 16));
                return;
            }
        }

        //-------------------------------------------------------------------------------------------------------------
        /// コンソールからキー入力する「JTAGもどき」のコマンドの振り分けと実行
        ///------------------------------------------------------------------------------------------------------------ 
        // コマンド自体は任意の文字列、コマンドの引数は0～0xFFFF_FFFF_FFFF_FFFFまでの値を最大2個添付可能
        enum InfType { Non, Byte, Word };
        String Do(string command, int arg1, int arg2) {
            uint sts = 0 ;
            int read_value = 0 ;
            InfType it = InfType.Non;

            //引数が2個のコマンド　引数の最大値は符号なし64bit整数の最大値。但し下位8bitもしくは16bitのみ有効。  
            if (arg2 != -1) {
                switch (command) {
                    default:    /* Error  */ return ("!Unknown command");
                }
            }
            //引数を1個伴うコマンド　引数の最大値は符号なし64bit整数の最大値。但し下位8bitもしくは16bitのみ有効。  
            else if (arg1 != -1) {
                switch (command) {
                    case "w":   /* Write MEM        */ sts = FT245.Flush(arg1, FT245.WriteMem); break;
                    case "e":   /* EXECUTE          */ sts = FT245.Flush(arg1, FT245.Execute); break;
                    case "sw":  /* SET WR           */ sts = FT245.Flush(arg1, FT245.SetWR); break;
                    case "sp":  /* SET PC           */ sts = FT245.Flush(arg1, FT245.SetPC); break;
                    case "sa":  /* SET ACC.         */ sts = FT245.Flush(arg1, FT245.SetAcc); break;
                    case "sf":  /* 直接FT245RLに値を設定する特殊命令 */
                        FT245.Put(arg1); sts = FT245.Flush(); break;
                    default:    /* Error            */ return ("!Unknown command");
                }
            }
            //引数を伴わないコマンド
            else {
                switch (command) {
                    case "o":   /* OPEN FT245        */ sts = FT245.Open(); break;
                    case "c":   /* CLOSE FT245       */ sts = FT245.Close(); break;
                    case "i":   /* INC. WR           */ sts = FT245.Flush(FT245.IncWR); break;
                    case "d":   /* DEC. WR           */ sts = FT245.Flush(FT245.DecWR); break;
                    case "e":   /* EXECUTE from 8000 */ sts = FT245.Flush(0x8000, FT245.Execute); break;
                    case "ep":  /* EXECUTE from PC   */ sts = FT245.Flush(FT245.Continue); break;
                    case "ez":  /* EXECUTE from 0000 */ sts = FT245.Flush(0x0000, FT245.Execute); break;
                    case "h":   /* HALT(SUSPENSION)  */ sts = FT245.Flush(FT245.Suspension); break;

                    case "pm":  /* PRESENT MEM on DATA-BUS */ sts = FT245.DisplayMemy(); break;
                    case "pp":  /* PRESENT PC on DATA-BUS  */ sts = FT245.DisplayPC(); break;
                    case "pa":  /* PRESENT Acc on DATA-BUS */ sts = FT245.DisplayAcc(); break;
                    case "pi":  /* PRESENT IN on DATA-BUS  */ sts = FT245.DisplaySW(); break;
                   
                    case "rm":  /* READ MEM.   */ sts = FT245.ReadMem(ref read_value); it = InfType.Word; break;
                    case "rp":  /* READ PC.    */ sts = FT245.ReadPC(ref read_value); it = InfType.Word; break;
                    case "ra":  /* READ ACC.   */ sts = FT245.ReadAcc(ref read_value); it = InfType.Word; break;
                    case "rf":  /* READ DIRECT */ sts = FT245.Get(ref read_value); it = InfType.Byte; break;

                    case "sw":  /* SET WR 8000 */ sts = FT245.Flush(FT245.SetWR); break;
                    case "sl":  /* SFT 0 → MSB */ sts = FT245.Flush(0, FT245.ShiftAcc); break;
                    case "sh":  /* SFT 1 → MSB */ sts = FT245.Flush(1, FT245.ShiftAcc); break;

                    ///以下はコンソール自体を制御するコマンド
                    case "clear": ShellPtr.ClearText(); /*コンソールをクリアする*/ return (""); //ShellTextBox.cs　line 82
                    case "add": ShellPtr.AddText("TestTestTest"); return ("");

                    default:    /* Error  */ return ("!Unknown command");
                }
            }

            if (sts != 0) return (string.Format("!Error sts = {0}.", sts));
            if (it == InfType.Non) return ("Ok.");
            if (it == InfType.Byte) return (string.Format("{0} ({0:X2}).", read_value & 0xFF));
            if (it == InfType.Word) return (string.Format("{0} ({0:X4}).", read_value & 0xFFFF));
            //if (it == InfType.Byte) return ( Convert.ToString(read_value & 0xFF, 16) + " (" + Convert.ToString(read_value & 0xFF) + ").");
            //if (it == InfType.Word) return ( Convert.ToString(read_value & 0xFFFF, 16) + " (" + Convert.ToString(read_value & 0xFFFF) + ").");
            else return ("???");

        }

    }
}

```
</div></details>




<details><summary>**ShellTextBox.csのソースコード**<br>黒三角印のクリックでShellTextBox.csのソースコードを参照できます</summary><div>

```cs

using System.Windows.Forms;
using System.Drawing;

namespace RETROF {
    ///補助クラス、EventArgsクラスを継承するためだけに存在
    public class ShellEventArgs : System.EventArgs {
        public string Command;
        public string Result;
    }

    ///独自クラス
    public partial class ShellTextBox : RichTextBox {
        ///プロパティ
        private string Prompt ="RETROF>";
        private const string LFCR ="\r\n";
        private const char CR ='\n';
        private const char COUTIONCHAR ='!';
        private Color ResultColor;
        private Color PromptColor;
        private Color CommandColor;
        private Color CoutionColor;

        ///デリゲートの宣言　（独自に継承したShellEventArgs型の引数を伴う）
        public delegate void ShellEventHandler(object sender, ShellEventArgs e);

        ///上記デリゲートのインスタンスを（イベント型として）定義
        public event ShellEventHandler KeyEnter;

        ///本クラスのコンストラクタ
        public ShellTextBox() {
            //RichTextBoxのアジア圏のフォント化け（公認バグ？）の抑制
            LanguageOption = RichTextBoxLanguageOptions.UIFonts;
            Prompt = ""; //仮設定、正式な設定はInitialize()で行う
        }

        ///初期化
        public void Initialize(string prompt, Color prompt_color = default(Color),
            Color command_color = default(Color), Color coution_color = default(Color)) {
            Prompt = prompt;
            PromptColor = (prompt_color != default(Color)) ? prompt_color : SelectionColor;
            CommandColor = (command_color != default(Color)) ? command_color : SelectionColor;
            CoutionColor = (coution_color != default(Color)) ? coution_color : Color.Red;
            ResultColor = SelectionColor;
            ShowPrompt();
        }

        ///MyEnterが空（==null）ではない事を確認し、「Enterキーが押されたイベント」を発行する
        protected virtual void OnConsole(ShellEventArgs e) {
            //KeyEnter(this, e);　//確認しないならこちらで十分
            KeyEnter?.Invoke(this, e);
        }

        ///（オーバーライド）マウスクリックによるカーソル移動を強制的に末尾にする
        protected override void OnMouseUp(MouseEventArgs e) {
            base.OnMouseUp(e);
            SelectionStart = int.MaxValue; //領域末尾を超す値の指定は領域末尾に移動する
        }

        ///（オーバーライド）矢印キー、BSキー、Enterキーの挙動変更
        protected override void OnKeyDown(KeyEventArgs e) {
            //カーソル上下移動の無効化
            if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down) { e.Handled = true; return; }
            //プロンプト文字列領域を侵すBackSpace(もしくはカーソル左移動)の無効化
            if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Back) {
                if (GetColumn() < Prompt.Length + 1) { e.Handled = true; return; }
            }
            if (e.KeyCode != Keys.Enter) return; //Enterキー以外なら終わり

            //以下はEnterキーが押下された場合のみ実行
            ShellEventArgs ee = new ShellEventArgs();
            ee.Command = GetCommand();　　　//コマンド（入力文字列）をShellEventArgsに設定
            OnConsole(ee);　　　            //実行　
            

            SelectionColor = ResultColor;   //実行結果の表示色を設定
            //表示文字列の先頭が「！」なら、その一文字を削除し、表示色をCoutionColorに変更
            if (ee.Result != null && ee.Result.Length != 0 && ee.Result[0] == COUTIONCHAR) {
                ee.Result = ee.Result.Remove(0, 1);
                SelectionColor = CoutionColor;
            }

            //画面が空(画面消去コマンドが発行された)でなければ結果を表示
            if (Text.Length !=0) AppendText(LFCR + ee.Result + LFCR);
            //次のプロンプトを出して終了
            ShowPrompt();
            e.Handled = true;
        }
        ///コンソールをクリアする関数とコンソールに文字を追加する関数
        public void ClearText() { Text = ""; }
        public void AddText(string text) { Text += text; }

        ///（視認性向上目的のprivateな補助関数）プロンプトを除く現在行の取り出し
        private string GetCommand() {
            return (Text.Substring(Text.Length - GetColumn()).Substring(Prompt.Length));
        }

        ///（視認性向上目的のprivateな補助関数）現在のカーソルのカラム位置を返す
        private int GetColumn() {
            return (SelectionStart - Text.LastIndexOf(CR) - 1);
        }

        ///（視認性向上目的のprivateな補助関数）プロンプトを指定色で表示する
        private void ShowPrompt() {
            SelectionColor = PromptColor;
            AppendText(Prompt);
            SelectionColor = CommandColor;
        }
    }
}


```
</div></details>

---
2021 がたろう