20201024のC#に関する記事は9件です。

OpenCVでプレイ動画の切り出しを自動化した話・GUI・他ソフト連携編

記事の目的

前回までで切り出し用のアルゴリズムは出来上がった。
今回はGUI面でどうすれば切り出しができるだろうかを考えた話をする。

(おさらい)作業内容

前回までで、ゲームのポーズがかかっている場所を抜き出すことができるようになった。
次の課題はWindowsアプリとしてどうやって関数を紐付けるか、そしてAviutlを操作するかだ。

GUI紐付け

今回はOxygen Not Includedのゲーム画面を抜き出すだけだけど、
今後もし同じような動画を作ることになった場合、切り出しの基準となる場所は当然異なってくる。

となると、最も良いのは任意の長方形を切り出し範囲にできることだと思う。
これをWindowsのGUIに落とし込むことを考える。

先に完成品ののGUIを出すが、このようなGUIなら直感的に切り出せると考えた。
GUI解説.png

正直、動画の表示周りの解説はOpenCVで作業をしようとしているのなら不要だろう。
これを実装するに当たり最も困るのは「ドラッグで切り出し範囲を指定」の部分だと思う。

これを現実的な時間で実装するために、今回はPictureBoxを継承したカスタムコントロールを使用した。

※カスタムコントロールの作り方はWinFormでボタンを継承したユーザコントロールを作るを参考にした。

カスタムしたPictureBoxの作成

今回のようにピクチャボックスを表示したい場合、考える必要がある点は2つだけだ。

  • どうやってドラッグ範囲を検出するかと、
  • どうやって表示するかだ。

ドラッグ範囲の抽出

まずはドラッグ範囲の検出。これはマウスイベントを取得すれば良い。
押された部分を検出するとなると、シンプルに考えるとこうなる。

        protected override void OnMouseDown(MouseEventArgs e)
        {
            StartPoint = e.Location;
            mousePressing = true;
            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (mousePressing)
            {
                EndPoint = e.Location;
                // コントロールの再描画要求
                Invalidate();
            }
            base.OnMouseMove(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (mousePressing)
            {
                mousePressing = false;
                EndPoint = e.Location;
            }
            base.OnMouseUp(e);
        }

        protected override void OnLeave(EventArgs e)
        {
            mousePressing = false;
            base.OnLeave(e);
        }

単純に、ピクチャーボックス上でマウスを押し始めたらマウスを監視し、離れたら監視を終了するだけ。
MouseEventArgsのLocationプロパティはピクチャボックスの左上を0,0としていたため、
ピクチャボックスの位置とかを意識する必要はなかった。
(枠は影響あるかもしれないが動画の画素数が大きかったために致命的な影響は出なかった)

OnMouseUpのif文やOnLeaveは不要なタイミングで入ってきた入力を防ぐだけだ。
これらが無いと、主にファイルを保存するダイアログのボタンをピクチャボックスの真上で押すと、
OnMouseUpだけがピクチャボックス上で発行されることになり、
切り出し範囲の表示がおかしくなる事がある。

これは単にダイアログを表示する前に切り出し位置を確定させてしまえば問題のない話だし、
動画の読み込み直後に位置がずれていてももう一度選択しなおせば良いだけなので、
運用上無くても問題は全くない。

これでドラッグした位置をStartPointとEndPointに格納することができた。

切り出し予定位置の表示

これはC++などでWindowsアプリを作った記憶がないと少しややこしいかもしれない。
PictureBoxに限らずWindowsフォームアプリでは、Paintイベントで「必要になったら」描画を行う。
基本的には、他のウィンドウに隠れた部分が出てきたりしたら描画を行う必要があり、Paintイベントを書き換えただけでは意図したタイミングでコントロールの描画を行ってくれない。
そのため今回はマウスを押した状態でPictureBoxの上を動かしたらInvalidate()を呼び出し、再描画を養成した。

あとは非同期処理用のBackGroundWorkerや切り出し判定を確認するためのChartを付け足したりしてるけど、
カスタムなどはしていないので省略。

Aviutl上の操作

Aviutlで動画のカット編集をする場合、カットしたいフレームを選択範囲として指定した上で、編集メニューの選択範囲を削除を選択すれば削除できる。
Aviutlはツールの制作なども盛んなため、指定したメニューを起動させるスクリプトもC言語で書かれている。
これをC#に書き直しても良かったが、選択範囲指定用のウィンドウの調査などが面倒だったので、キーコードを原始的に送る形にした。

Aviutlの準備

Aviutlはほぼすべてのメニューに対してショートカットキーを紐付けることができる。
今回は選択範囲の指定、選択範囲を削除にCtrl+Shift+EとDを指定した。
Aviutlショートカット.png

自分の環境で選択範囲の指定を選択したとき、次のような画面が出る。
Aviutlショートカット2.png

Tabキー2回で選択開始フレームのテキストボックスにフォーカスが合い、テキストが全選択されている状態だったためキーコードを送信すれば選択開始フレームは編集できた。
同様に選択終了フレームも編集できる。

キーコードの送信

当然の話だが、動画の頭をカットしたとき、カットした後の再生位置の動画は前に詰める。要は普通に順番にカットしようとしたら2つ目以降の選択範囲がずれてしまう。
今回は計算が面倒だったので、後ろから順番にカット編集することにした。

ファイルを読み込んで、カットする範囲を後ろからになるように調整してからSendKeys.SendWait()でキーコードを送ったのが下記の部分。
キーに対応する文字を確認するのには英語のリファレンスを参照する必要があった。
(日本語のリファレンスは機械翻訳なせいでまともな情報が見れなかった)

            List<string[]> inputFrames = new List<string[]>();
            using (StreamReader reader = new StreamReader(openKeyDialog.FileName))
            {
                string line;
                while((line = reader.ReadLine()) != null){
                    inputFrames.Insert(0, line.Split(','));
                }
            }

            foreach (string[] keyFramesPair in inputFrames)
            {
                SendKeys.SendWait("+^E");
                System.Threading.Thread.Sleep(10);
                SendKeys.SendWait("{TAB}{TAB}");
                System.Threading.Thread.Sleep(10);
                SendKeys.SendWait(keyFramesPair[0]);
                System.Threading.Thread.Sleep(10);
                SendKeys.SendWait("{TAB}");
                System.Threading.Thread.Sleep(10);
                SendKeys.SendWait(keyFramesPair[1]);
                System.Threading.Thread.Sleep(10);
                SendKeys.SendWait("~");
                System.Threading.Thread.Sleep(10);
                SendKeys.SendWait("+^D");
                System.Threading.Thread.Sleep(10);
            }

            MessageBox.Show("完了");

Ctrl+Shift+E→Tab2回→選択開始の位置を入力→Tab一回→選択終了の位置を入力→Enter→Ctrl+Shift+Dで一箇所カットできるので、
それを入力された行数だけ繰り返した。

どう考えてもSleep(10)はお行儀が良い処理ではないが、止める時間がたかが知れているのと、入力同士の間隔をある程度開けないとAviutlの処理が間に合わないことから今回は原始的にsleepさせる事になった。

最後に

https://github.com/mickie895/ONI_Movie_Cutter
今回作成したソースは上記にまとめました。

今回、動画の編集作業を自動化させることにより、手作業で5時間かかった動画作成の前準備を拘束時間1分に短縮させることができた。
これをきっかけに動画の編集処理などを自動化させられないか考える人が増えたら幸いである。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#でAwaitableなタイマーを作成する

TaskCompletionSourceを利用して、System.Timers.Timerにインターバル時間の経過を待機できるタスクを追加しました。

開発・実行環境
Visual Studio 2019 Community
.Net Framework 4.7.2
C# 7.3

イベントベース非同期処理をタスクベース非同期に変換する

一定の時間間隔で何らかの処理を行いたいとき、タイマーを利用することが多々あります。
私はこのような目的でよくSystem.Timers.Timerを利用します。このタイマーは、Intervalプロパティで指定したインターバル時間毎にElapsedイベントを発行することで、利用者に時間経過を通知します。イベントベースで処理する場合、例えば一定回数だけイベントが発行されたらタイマーを停止して処理をやめる、といったことを行いたい場合、コードが煩雑になりがちで、また処理の流れに沿った直感的なコーディングは難しいです。そこで、TaskCompletionSourceを利用してイベントをタスクに変換する方法をつい最近(今更)知ったので、Elapsedイベントを待機できるタスクを追加したAwaitableTimerクラスを作成してみました。

AwaitableTimer.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace TimerSample
{
    /// <summary>
    /// <see cref="System.Timers.Timer"/>のインターバル時間の経過イベントを待機できるタスクを提供します。
    /// </summary>
    public class AwaitableTimer : System.Timers.Timer
    {
        #region field

        private TaskCompletionSource<DateTime> _tcs;

        #endregion

        #region constructor

        /// <summary>
        /// デフォルトコンストラクタ
        /// </summary>
        public AwaitableTimer() : base()
        {
            Elapsed += OnElapsed;
        }

        /// <summary>
        /// インターバル時間、およびインターバル経過イベントを繰り返し発生させるかどうかを指定してタイマーを作成します。
        /// </summary>
        /// <param name="interval"></param>
        /// <param name="autoReset"></param>
        public AwaitableTimer(TimeSpan interval, bool autoReset) : base(interval.TotalMilliseconds)
        {
            AutoReset = autoReset;
            Elapsed += OnElapsed;
        }

        /// <summary>
        /// インターバル時間を指定して初期化
        /// </summary>
        /// <param name="interval"></param>
        public AwaitableTimer(TimeSpan interval) : base(interval.TotalMilliseconds)
        {
            Elapsed += OnElapsed;
        }

        #endregion

        private void OnElapsed(object sender, ElapsedEventArgs e)
        {
            if (_tcs != null)
            {
                _tcs.TrySetResult(e.SignalTime);
                _tcs = null;
            }
        }

        private void OnTaskCanceled()
        {
            if (_tcs != null)
            {
                _tcs.TrySetException(new TaskCanceledException(_tcs.Task));
                _tcs = null;
            }
        }

        /// <summary>
        /// インターバル時間とインターバル経過イベントを繰り返し発生させるかどうかを指定して、タイマーを開始します。
        /// </summary>
        /// <param name="interval"></param>
        /// <param name="autoReset"></param>
        /// <returns></returns>
        public static AwaitableTimer StartNew(TimeSpan interval, bool autoReset)
        {
            var timer = new AwaitableTimer(interval, autoReset);
            timer.Start();
            return timer;
        }

        /// <summary>
        /// タイマーの経過時間をリセットして再開します
        /// </summary>
        public void Restart()
        {
            Stop();
            Start();
        }

        /// <summary>
        /// インターバル経過イベントを待機するタスク。タイマーが止まっている場合は、即時に<see cref="DateTime.Now"/>を返します。
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public async Task<DateTime> WaitElapsedAsync(CancellationToken token = default)
        {
            if (Enabled)
            {
                _tcs = new TaskCompletionSource<DateTime>(TaskCreationOptions.RunContinuationsAsynchronously);

                var register = token.Register(OnTaskCanceled);

                using (register)
                {
                    return await _tcs.Task;
                }
            }
            else
            {
                return DateTime.Now;
            }
        }
    }
}

例えば以下のように使います。この例では指定した時間間隔で、指定した回数だけ何らかの処理(ここではAsyncMethod)を行います。何らかの理由でインターバル時間内に処理が終わっていなかったらタイムアウトとし、ループを抜け出します。時間内に処理が終わったら、次のインターバルを待ちます。

using System;
using static System.Console;
using System.Threading.Tasks;

namespace TimerSample
{
    class Program
    {
        static readonly Random Random = new Random();

        static async Task Main(string[] args)
        {
            while (true)
            {
                if (ReadKey().Key == ConsoleKey.Escape)
                    break;

                Clear();

                WriteLine("インターバル時間をミリ秒で指定");

                if (!double.TryParse(ReadLine(), out var interval))
                    continue;

                WriteLine("繰り返し回数を指定");
                if (!int.TryParse(ReadLine(), out var n))
                    continue;

                WriteLine("不具合の起こる確率を指定");
                if (!double.TryParse(ReadLine(), out var p))
                    continue;

                bool timeout = false;
                bool isRunning = false;

                async Task AsyncMethod()
                {
                    int delay = 0;

                    if (Random.NextDouble() > p)
                    {
                        delay = (int)(0.5 * interval);
                    }
                    else
                    {
                        delay = (int)(2 * interval);
                    }

                    isRunning = true;
                    await Task.Delay(delay);
                    isRunning = false;
                }

                using (var intervalTimer = new AwaitableTimer(TimeSpan.FromMilliseconds(interval), true))
                {
                    intervalTimer.Elapsed += (s, e) =>
                    {
                        if (isRunning)
                        {
                            timeout = true;
                        }
                    };

                    intervalTimer.Start();
                    var startTime = DateTime.Now;
                    var previous = startTime;

                    for (var i = 0; i < n; i++)
                    {
                        await AsyncMethod();

                        if (timeout)
                        {
                            WriteLine("インターバル時間内に処理が終わりませんでした!");
                            timeout = false;
                            break;
                        }

                        var now = await intervalTimer.WaitElapsedAsync();
                        var period = now - previous;
                        previous = now;
                        WriteLine($"ラップ{i + 1:000} 経過時間(トータル):{(now - startTime).TotalMilliseconds:f2} ms 経過時間(インターバル):{period.TotalMilliseconds:f2} ms");
                    }
                }

                WriteLine("終了");
            }
        }
    }
}

Elapsedイベントを利用したタイムアウト判定と、async/awaitを利用したインターバル時間経過の待機を併用しています。全てイベントベースで書くよりもいくらか簡単かつ直感的になった気がします。

実行結果

インターバル時間をミリ秒で指定
1000
繰り返し回数を指定
20
不具合の起こる確率を指定
0.1
ラップ001 経過時間(トータル):1000.15 ms 経過時間(インターバル):1000.15 ms
ラップ002 経過時間(トータル):2000.73 ms 経過時間(インターバル):1000.57 ms
ラップ003 経過時間(トータル):3002.09 ms 経過時間(インターバル):1001.36 ms
ラップ004 経過時間(トータル):4002.23 ms 経過時間(インターバル):1000.14 ms
ラップ005 経過時間(トータル):5002.55 ms 経過時間(インターバル):1000.32 ms
インターバル時間内に処理が終わりませんでした!
終了

参考資料

タスクとイベント ベースの非同期パターン (EAP)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

G3KHコア向けのバイナリから命令コードを逆アセンブルするプログラムをフルスクラッチで書いてみた - C#

画面キャプチャ

image.png

当然無保証!

使い方

以下のソースコードをコンパイルして出来上がったG3khDisAsm.exeに、G3KHマイコンコア向けのソフトウェアのバイナリファイルをぶち込む。(先頭が0番地である前提で作ってあるので注意。)

何もファイルを指定しないと、サンプル用のダミーデータが代わりに採用されます。

C#ソースコード

本体

G3khDisAsm.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;


class G3khDisAsm : Form
{
    AsmListView asmListView;

    G3khDisAsm(string path)
    {
        ClientSize = new Size(800, 600);
        Text = "【無保証・非公式】 G3KH Disassembler (Unofficial)";

        Controls.Add(asmListView = new AsmListView(){ Dock = DockStyle.Fill });

        byte[] buf;
        if ( path == null ) {
            buf = FillDummyDataForSample();
        }
        else{
            buf = LoadBinFile(path);
        }
        asmListView.ReloadAsmView(buf, 0);
    }


    byte[] LoadBinFile(string binFilePath)
    {
        byte[] buf = File.ReadAllBytes(binFilePath);
        return buf;
    }

    byte[] FillDummyDataForSample()
    {
        byte[] buf = new byte[256];
        for(int i=0;i<256;i++){
            buf[i] = (byte)i;
        }
        return buf;
    }


    [STAThread]
    static void Main(string[] args)
    {
        string path = null;
        if ( args.Length == 1 ) {
            path = args[0];
        }
        Application.Run(new G3khDisAsm(path));
    }
}

表示用

AsmListView.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;


public class AsmItemAnInstruction
{

    public long        Address{get;private set;}
    public Instruction Inst{get;private set;}

    public string      CachedDesc{get;private set;}
    public long        CachedJumpDestAddr{get;private set;}

    public AsmItemAnInstruction(long addr, Instruction inst)
    {
        Address = addr;
        Inst    = inst;

        CachedDesc         = inst.GetDescription();
        CachedJumpDestAddr = inst.GetAbsoluteJumpDestinationIfJump(addr); // PC は instruction の addrそのもの
    }
}


public class AsmListView : ListView
{
    // Currently Containing Parsed Data Information
    public long StartAddr        {get; private set;}
    public long BufferedProgSize {get; private set;} // in Bytes

    public long EndAddrPlus1
    {
        get{return StartAddr + BufferedProgSize;}
    }


    public AsmListView()
    {
        StartAddr = 0;
        BufferedProgSize = 0;

        InitializeProperties();
        InitializeColumns();
    }

    void InitializeProperties()
    {
        this.View = View.Details;
        this.MultiSelect = false;
        this.FullRowSelect = true;
        this.HideSelection = false;
        this.GridLines = true;
        this.Font = new Font("MS ゴシック", 9);
    }

    void InitializeColumns()
    {
        this.Columns.Add("Address",      60, HorizontalAlignment.Left);
        this.Columns.Add("[1]",          60, HorizontalAlignment.Left);
        this.Columns.Add("[0]",          60, HorizontalAlignment.Left);
        this.Columns.Add("[3]",          60, HorizontalAlignment.Left);
        this.Columns.Add("[2]",          60, HorizontalAlignment.Left);
        this.Columns.Add("[5]",          60, HorizontalAlignment.Left);
        this.Columns.Add("[4]",          60, HorizontalAlignment.Left);
        this.Columns.Add("[7]",          60, HorizontalAlignment.Left);
        this.Columns.Add("[6]",          60, HorizontalAlignment.Left);
        this.Columns.Add("Instruction", 160, HorizontalAlignment.Left);
        this.Columns.Add("Dest",         60, HorizontalAlignment.Left);
    }

    public void ReloadAsmView(byte[] a, long startAddr)
    {
        this.Items.Clear();

        List<Instruction> insts = null;
        if ( a != null ) {
            insts = DisAssembler.ParseAsInstructions(startAddr, a, 0);
        }

        if (insts != null) {
            long addr = startAddr;
            int ofs = 0;

            this.BeginUpdate();
            try {
                foreach ( Instruction inst in insts ) {
                    var asmItem = new AsmItemAnInstruction(addr, inst);
                    this.Items.Add(MakeAsmListViewItem(asmItem));

                    addr += inst.Size;
                    ofs  += inst.Size;
                }
            }
            finally {
                this.EndUpdate();
            }

            StartAddr = startAddr;
            BufferedProgSize = ofs;
        }
    }

    ListViewItem MakeAsmListViewItem(AsmItemAnInstruction asmItem)
    {
        string jumpAddrInStr = "";

        if ( asmItem.Inst.HasJump && asmItem.CachedJumpDestAddr >= 0 ) {
            jumpAddrInStr = asmItem.CachedJumpDestAddr.ToString("X08");
        }

        byte[] b = asmItem.Inst.Bytes;
        if ( b == null ) { b = new byte[0]; }

        var item = new ListViewItem(new string[]{
            asmItem.Address.ToString("X8"),
            b.Length<2 ? "" : Convert.ToString(b[1], 2).PadLeft(8, '0'),
            b.Length<2 ? "" : Convert.ToString(b[0], 2).PadLeft(8, '0'),
            b.Length<4 ? "" : Convert.ToString(b[3], 2).PadLeft(8, '0'),
            b.Length<4 ? "" : Convert.ToString(b[2], 2).PadLeft(8, '0'),
            b.Length<6 ? "" : Convert.ToString(b[5], 2).PadLeft(8, '0'),
            b.Length<6 ? "" : Convert.ToString(b[4], 2).PadLeft(8, '0'),
            b.Length<8 ? "" : Convert.ToString(b[7], 2).PadLeft(8, '0'),
            b.Length<8 ? "" : Convert.ToString(b[6], 2).PadLeft(8, '0'),
            asmItem.CachedDesc,
            jumpAddrInStr,
        });
        item.Tag = asmItem;

        return item;
    }
}

CPUレジスタセット

CpuRegs.cs
using System;
using System.Collections.Generic;


public class CpuRegs
{
    public static readonly int GRegN = 32;

    static readonly string[] GRegNames = new string[]{
        "r0",
        "r1",
        "r2",
        "sp",// r3
        "gp",// r4
        "tp",// r5
        "r6",        "r7",        "r8",        "r9",
        "r10",       "r11",       "r12",       "r13",
        "r14",       "r15",       "r16",       "r17",
        "r18",       "r19",       "r20",       "r21",
        "r22",       "r23",       "r24",       "r25",
        "r26",       "r27",       "r28",       "r29",
        "ep",// r30
        "lp",// r31
    };


    public static string GetGRegName(long index)
    {
        if ( index >= 0 && index < GRegN ) {
            return GRegNames[index];
        }
        throw new Exception("out of range access to reg#"+index.ToString());
    }

    public static int GetGRegIndexByName(string name)
    {
        for ( int i=0; i<GRegN; i++ ) {
            if ( GRegNames[i] == name ) {
                return i;
            }
        }
        return -1;
    }


    ////////////////////////////////
    //
    //  System Registers
    //

    public static readonly int SRegIDN = 32;
    public static readonly int SSelIDN = 8;

    public static string GetSysRegName(long regID, long selID)
    {
        return SysRegHandler.GetName(regID, selID);
    }

    ////////////////////////////////////////////////////////////////

    class SysRegHandler
    {
        public static string GetName(long regID, long selID)
        {
            SysRegDef t = GetDefByID(regID, selID);
            if ( t == null ) {
                return "["+regID.ToString()+","+selID.ToString()+"](unknown)";
            }
            else {
                return t.Name;
            }
        }

        public static SysRegDef GetDefByID(long regID, long selID)
        {
            foreach ( var t in SysRegDefs ) {
                if ( t.RegID == regID && t.SelID == selID ) {
                    return t;
                }
            }
            return null;
        }

        public class SysRegDef
        {
            public string Name{get;private set;}
            public int RegID{get;private set;}
            public int SelID{get;private set;}

            public SysRegDef(string name, int regID, int selID)
            {
                Name  = name;
                RegID = regID;
                SelID = selID;
            }
        }


        public static readonly SysRegDef[] SysRegDefs = new SysRegDef[] {
            //            名称    regID selID
            new SysRegDef("EIPC"   ,  0,  0 ),
            new SysRegDef("EIPSW"  ,  1,  0 ),
            new SysRegDef("FEPC"   ,  2,  0 ),
            new SysRegDef("FEPSW"  ,  3,  0 ),
            new SysRegDef("PSW"    ,  5,  0 ),
            new SysRegDef("FPSR"   ,  6,  0 ),
            new SysRegDef("FPEPC"  ,  7,  0 ),
            new SysRegDef("FPST"   ,  8,  0 ),
            new SysRegDef("FPCC"   ,  9,  0 ),
            new SysRegDef("FPCFG"  , 10,  0 ),
            new SysRegDef("FPEC"   , 11,  0 ),
            new SysRegDef("EIIC"   , 13,  0 ),
            new SysRegDef("FEIC"   , 14,  0 ),
            new SysRegDef("CTPC"   , 16,  0 ),
            new SysRegDef("CTPSW"  , 17,  0 ),
            new SysRegDef("CTBP"   , 20,  0 ),
            new SysRegDef("EIWR"   , 28,  0 ),
            new SysRegDef("FEWR"   , 29,  0 ),

            new SysRegDef("MCFG0"  ,  0,  1 ),
            new SysRegDef("RBASE"  ,  2,  1 ),
            new SysRegDef("EBASE"  ,  3,  1 ),
            new SysRegDef("INTBP"  ,  4,  1 ),
            new SysRegDef("MCTL"   ,  5,  1 ),
            new SysRegDef("PID"    ,  6,  1 ),
            new SysRegDef("FPIPR"  ,  7,  1 ),
            new SysRegDef("SCCFG"  , 11,  1 ),
            new SysRegDef("SCBP"   , 12,  1 ),

            new SysRegDef("HTCFG0" ,  0,  2 ),
            new SysRegDef("MEA"    ,  6,  2 ),
            new SysRegDef("ASID"   ,  7,  2 ),
            new SysRegDef("MEI"    ,  8,  2 ),
            new SysRegDef("ISPR"   , 10,  2 ),
            new SysRegDef("PMR"    , 11,  2 ),
            new SysRegDef("ICSR"   , 12,  2 ),
            new SysRegDef("INTCFG" , 13,  2 ),

            new SysRegDef("MPM"    ,  0,  5 ),
            new SysRegDef("MPRC"   ,  1,  5 ),
            new SysRegDef("MPBRGN" ,  4,  5 ),
            new SysRegDef("MPTRGN" ,  5,  5 ),
            new SysRegDef("MCA"    ,  8,  5 ),
            new SysRegDef("MCS"    ,  9,  5 ),
            new SysRegDef("MCC"    , 10,  5 ),
            new SysRegDef("MCR"    , 11,  5 ),

            new SysRegDef("MPLA0"  ,  0,  6 ),
            new SysRegDef("MPUA0"  ,  1,  6 ),
            new SysRegDef("MPAT0"  ,  2,  6 ),
            new SysRegDef("MPLA1"  ,  4,  6 ),
            new SysRegDef("MPUA1"  ,  5,  6 ),
            new SysRegDef("MPAT1"  ,  6,  6 ),
            new SysRegDef("MPLA2"  ,  8,  6 ),
            new SysRegDef("MPUA2"  ,  9,  6 ),
            new SysRegDef("MPAT2"  , 10,  6 ),
            new SysRegDef("MPLA3"  , 12,  6 ),
            new SysRegDef("MPUA3"  , 13,  6 ),
            new SysRegDef("MPAT3"  , 14,  6 ),
            new SysRegDef("MPLA4"  , 16,  6 ),
            new SysRegDef("MPUA4"  , 17,  6 ),
            new SysRegDef("MPAT4"  , 18,  6 ),
            new SysRegDef("MPLA5"  , 20,  6 ),
            new SysRegDef("MPUA5"  , 21,  6 ),
            new SysRegDef("MPAT5"  , 22,  6 ),
            new SysRegDef("MPLA6"  , 24,  6 ),
            new SysRegDef("MPUA6"  , 25,  6 ),
            new SysRegDef("MPAT6"  , 26,  6 ),
            new SysRegDef("MPLA7"  , 28,  6 ),
            new SysRegDef("MPUA7"  , 29,  6 ),
            new SysRegDef("MPAT7"  , 30,  6 ),

            new SysRegDef("MPLA8"  ,  0,  7 ),
            new SysRegDef("MPUA8"  ,  1,  7 ),
            new SysRegDef("MPAT8"  ,  2,  7 ),
            new SysRegDef("MPLA9"  ,  4,  7 ),
            new SysRegDef("MPUA9"  ,  5,  7 ),
            new SysRegDef("MPAT9"  ,  6,  7 ),
            new SysRegDef("MPLA10" ,  8,  7 ),
            new SysRegDef("MPUA10" ,  9,  7 ),
            new SysRegDef("MPAT10" , 10,  7 ),
            new SysRegDef("MPLA11" , 12,  7 ),
            new SysRegDef("MPUA11" , 13,  7 ),
            new SysRegDef("MPAT11" , 14,  7 ),
            new SysRegDef("MPLA12" , 16,  7 ),
            new SysRegDef("MPUA12" , 17,  7 ),
            new SysRegDef("MPAT12" , 18,  7 ),
            new SysRegDef("MPLA13" , 20,  7 ),
            new SysRegDef("MPUA13" , 21,  7 ),
            new SysRegDef("MPAT13" , 22,  7 ),
            new SysRegDef("MPLA14" , 24,  7 ),
            new SysRegDef("MPUA14" , 25,  7 ),
            new SysRegDef("MPAT14" , 26,  7 ),
            new SysRegDef("MPLA15" , 28,  7 ),
            new SysRegDef("MPUA15" , 29,  7 ),
            new SysRegDef("MPAT15" , 30,  7 ),
        };
    }
}

命令コードデコーダ

作りかけ(一部命令のテキスト表示が未実装)

DisAssembler.cs
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;


public class DisAssembler
{
    public static Instruction ParseAsAnInstruction(long pc, byte[] buf, int offsetI)
    {
        if ( buf == null ) { return null; }
        return Instruction.ParseBytes(buf, offsetI);
    }

    public static List<Instruction> ParseAsInstructions(long pc, byte[] buf, int offsetI)
    {
        if ( buf == null ) { return null; }
        var ret = new List<Instruction>();

        while ( offsetI+1 < buf.Length ) { // G3KH の命令は最小でも 2byte なので左辺を +1 してある
            var inst = ParseAsAnInstruction(pc, buf, offsetI);
            ret.Add(inst);
            offsetI += inst.Size;
            pc += inst.Size;
        }

        return ret;
    }
}


public class Instruction
{
    public static readonly int MaxInstSize = 8;
    static readonly int SizeForUnknown = 2;

    int    _instIdOfDef; // -1 means unknown

    public int Size { get; private set; } //return (_instIdOfDef < 0) ? SizeForUnknown : Ope.Size;
    public byte[] Bytes { get; private set; }
    public bool HasJump { get{ return (_instIdOfDef < 0) ? false : Ope.HasJump; } }
    OpcodeDef Ope { get{ return OpcodeDef.Opcodes[_instIdOfDef]; } }

    // for unknown instruction
    Instruction()
    {
        _instIdOfDef = -1;
        Size = SizeForUnknown;
        Bytes = null;
    }

    // 
    Instruction(int opeDefinedIndex, byte[] buf, long offsetI)
    {
        int instSize = OpcodeDef.Opcodes[opeDefinedIndex].Size;

        _instIdOfDef = opeDefinedIndex;
        Size = instSize;

        Bytes = new byte[instSize];
        Array.Copy(buf, offsetI, Bytes, 0, instSize); // srcArray, srcI, destArray, destI, len
    }



    public static Instruction ParseBytes(byte[] buf, long offsetI)
    {
        if ( buf == null ) {
            throw new Exception("Instruction.ParseBytes(null, offsetI="+offsetI.ToString()+") is called.");
        }

        for ( int i=0 ; i<OpcodeDef.Opcodes.Length ; i++ ) {
            if ( OpcodeDef.Opcodes[i].IsMatch(buf, offsetI) ) {
                return new Instruction(i, buf, offsetI);
            }
        }

        return new Instruction(); // unknown instruction
    }

    public string GetDescription()
    {
        if ( _instIdOfDef >= 0 ) {
            return OpcodeDef.Opcodes[_instIdOfDef].GetDesc(this);
        }
        return "(parse failed)";
    }

    // 
    public long GetAbsoluteJumpDestinationIfJump(long pc)
    {
        long jumpAddr = -1;
        if ( _instIdOfDef >= 0 ) {
            if ( HasJump ) {
                jumpAddr = OpcodeDef.Opcodes[_instIdOfDef].GetAbsoluteJumpAddr(this, pc);
            }
        }
        return jumpAddr;
    }

    enum FormatId {
        I, IIa, IIb, III,
        IVa, IVb, // 3words
        V, VIa, VIb, VII, VIII,
        IX, X, XI, XII,
        XIIIa,
        XIIIb, // 3words
        XIIIc, // 4words
        XIVa,
        XIVb, // 3words
        Copro,
    };

    class OpcodeDef
    {
        static readonly int BitsOfInstWord = 16;

        public bool HasJump   {get;private set;}

        public string   Name   {get;private set;}
        public string   OpeStr1{get;private set;}
        public string   OpeStr2{get;private set;}
        public FormatId Fmt    {get;private set;}

        public int Size {
            get{
                if ( Fmt == FormatId.I   ||
                     Fmt == FormatId.IIa ||
                     Fmt == FormatId.IIb ||
                     Fmt == FormatId.III ||
                     Fmt == FormatId.IVa ||
                     Fmt == FormatId.IVb   ) {
                    return 2;
                 }
                 else if ( Fmt == FormatId.VIb  ||
                           Fmt == FormatId.XIVb ||
                           Fmt == FormatId.XIIIb) {
                    return 6;
                 }
                 else if ( Fmt == FormatId.XIIIc ) {
                    return 8;
                 }
                 else {
                    return 4;
                 }
            }
        }

        // 前提: t の bit位置 signedBitPos より左側の bit は立っていないものとする
        static long ExtendSign(long t, int signedBitPos)
        {
            if ( signedBitPos < 0 ) { return t; }

            if ( ( (t >> signedBitPos) & 1 ) == 0 ) {
                return t;
            }
            return t - (2L << signedBitPos); // 2倍の重みで引く
        }


        /////////////////////////////////////////////
        // Delegate function table structure

        public delegate string DescFunc       (Instruction inst);          // to make description
        public delegate long   AbsJumpCalcFunc(Instruction inst, long pc); // to calc jump address ( independent with CpuReg other than PC )

        DescFunc        _descFunc;
        AbsJumpCalcFunc _absJumpCalcFunc;

        /////////////////////////////////////////////
        // クラス外部からの呼び出し用

        public string GetDesc(Instruction inst)
        {
            return _descFunc(inst);
        }

        public long GetAbsoluteJumpAddr(Instruction inst, long pc)
        {
            return _absJumpCalcFunc(inst, pc);
        }

        ////////////////////////////////////////////////////////////////////////////////
        // for JumpCalcFunc
        //
        static long DummyFunc(Instruction inst, long pc)
        {
            return -1;
        }

        static long RelJmp(Instruction inst, long pc)
        {
            int cnt;
            long d = ParseCharInOpe(inst, 'd', out cnt);
            d = ExtendSign(d, cnt-1);
            return (uint)(pc + 2*d);
        }

        static long RelLoop(Instruction inst, long pc)
        {
            long d = ParseCharInOpe(inst, 'd');
            return (uint)(pc - 2*d);
        }

        static long RelJmp32(Instruction inst, long pc)
        {
            byte[] buf = inst.Bytes;

            int cnt;
            long d = ParseCharInOpe(inst, 'd', out cnt);

            byte lo = buf[4];
            byte hi = buf[5];
            long dUpper = ((int)hi << 8) | lo;

            d |= (dUpper<<cnt);
            cnt+=16;

            d = ExtendSign(d, cnt-1);
            return (uint)(pc + 2*d);
        }

        ////////////////////////////////////////////////////////////////////////////////
        // for DescFunc
        //
        static string DescText(Instruction inst)
        {
            return inst.Ope.Name;
        }

        static string DescReg123(Instruction inst)
        {
            string reg1 = CpuRegs.GetGRegName(GetReg1Id(inst));//ParseCharInOpe(inst, 'R'));
            string reg2 = CpuRegs.GetGRegName(GetReg2Id(inst));//ParseCharInOpe(inst, 'r'));
            string reg3 = CpuRegs.GetGRegName(GetReg3Id(inst));//ParseCharInOpe(inst, 'w'));
            string t = inst.Ope.Name;
            t = Regex.Replace(t, @"\breg1\b", reg1);
            t = Regex.Replace(t, @"\breg2\b", reg2);
            t = Regex.Replace(t, @"\breg3\b", reg3);
            return t;
        }


        static string DescReg123Imm5s(Instruction inst) { return _Core_DescReg123Imm5(inst, true);  } // signed imm
        static string DescReg123Imm5u(Instruction inst) { return _Core_DescReg123Imm5(inst, false); } // unsigned imm

        static string DescReg123Imm9s(Instruction inst) { return _Core_DescReg123Imm9(inst, true);  } // signed imm
        static string DescReg123Imm9u(Instruction inst) { return _Core_DescReg123Imm9(inst, false); } // unsigned imm

        static string DescReg123Imm16s(   Instruction inst) { return _Core_DescReg123Imm16(inst, true , false);  } //   signed imm  dec
        static string DescReg123Imm16u(   Instruction inst) { return _Core_DescReg123Imm16(inst, false, false);  } // unsigned imm  dec
        static string DescReg123Imm16uHex(Instruction inst) { return _Core_DescReg123Imm16(inst, false, true);   } // unsigned imm  hex

        static string DescReg123Disp4u( Instruction inst){ return _Core_DescReg123DispN(inst, 4, false); }
        static string DescReg123Disp5u( Instruction inst){ return _Core_DescReg123DispN(inst, 5, false); }
        static string DescReg123Disp7u( Instruction inst){ return _Core_DescReg123DispN(inst, 7, false); }
        static string DescReg123Disp8u( Instruction inst){ return _Core_DescReg123DispN(inst, 8, false); }
        static string DescReg123Disp9s( Instruction inst){ return _Core_DescReg123DispN(inst, 9, true);  } // signed disp
        static string DescReg123Disp16s(Instruction inst){ return _Core_DescReg123DispN(inst,16, true);  } // signed disp

        static string DescReg123Disp16sB(Instruction inst){ return _Core_DescReg123Disp16b(inst, true);  } // signed disp
        static string DescReg123Disp23s(Instruction inst){ return _Core_DescReg123Disp23(inst, true);  } // signed disp
        static string DescReg123Disp23u(Instruction inst){ return _Core_DescReg123Disp23(inst, false); } // unsigned disp

        static string DescReg123Imm32(Instruction inst)
        {
            string t = DescReg123(inst);
            t = Regex.Replace(t, @"\bimm32\b", ((uint)GetImm32Value(inst)).ToString("X08")+"h");
            return t;
        }


        static string DescReg123Bit3Disp16(Instruction inst)
        {
            string t = _Core_DescReg123DispN(inst, 16, true);
            t = Regex.Replace(t, @"\bbit#3\b", "bit" + ParseCharInOpe(inst, 'b').ToString());
            return t;
        }


        static string DescDispose(Instruction inst)
        {
            string t = _Core_DescReg123Imm5(inst, false);
            t = Regex.Replace(t, @"\blist12\b", subFuncList12ToString(inst));
            return t;
        }

        static string DescPrepare2word(Instruction inst)
        {
            string t = _Core_DescReg123Imm5(inst, false);
            t = Regex.Replace(t, @"\blist12\b", subFuncList12ToString(inst));
            return t;
        }

        static string DescPrepare_Imm16(Instruction inst)
        {
            long imm16 = subFuncGetValueForEpOfPrepare(inst);
            string t = _Core_DescReg123Imm5(inst, false);
            t = Regex.Replace(t, @"\blist12\b", subFuncList12ToString(inst));
            t = Regex.Replace(t, @"\bimm16\b" , (imm16<0)?"(parse error)":(imm16.ToString("X08")+"h"));
            return t;
        }
        static string DescPrepare_Imm32(Instruction inst)
        {
            long imm32 = subFuncGetValueForEpOfPrepare(inst);
            string t = _Core_DescReg123Imm5(inst, false);
            t = Regex.Replace(t, @"\blist12\b", subFuncList12ToString(inst));
            t = Regex.Replace(t, @"\bimm32\b" , (imm32<0)?"(parse error)":(imm32.ToString("X08")+"h"));
            return t;
        }


        static string subFuncList12ToString(Instruction inst)
        {
            List<byte> regIds = subFuncList12ToRegIds(inst);
            var sb = new StringBuilder();
            for (int i=0;i<regIds.Count;i++) {
                if(i>0){sb.Append(",");}
                sb.Append(CpuRegs.GetGRegName(regIds[i]));
            }
            return sb.ToString();
        }

        static bool subFuncHasFfOfPrepare(Instruction inst)
        {
            byte[] buf = inst.Bytes;
            // byte1    byte0     byte3    byte2
            // xxxxxxxx xxxxxxxx  xxxxxxxx xxxxxxXx
            if ( buf.Length < 2 ) { return false; } // invalid
            return ((buf[2]>>1) & 0x01) == 1;
        }

        // return -1 for use sp  or error
        static long subFuncGetValueForEpOfPrepare(Instruction inst)
        {
            byte[] buf = inst.Bytes;
            int ff = subFuncGetFfOfPrepare(buf);

            // ff = 00: ep ← sp
            // ff = 01: ep ← sign-extend (imm16)
            // ff = 10: ep ← imm16 logically shift left by 16
            // ff = 11: ep ← imm32
            switch ( ff ) {
            case 0: return -1;
            case 1: if (buf.Length < 6) { return -1; } else {return (long)(uint)(short)( (((uint)buf[5])<<8) | buf[4] ); }
            case 2: if (buf.Length < 6) { return -1; } else {return (long)(uint) ( ((((uint)buf[5])<<8) | buf[4]) << 16 ); }
            case 3: if (buf.Length < 8) { return -1; } else {return (long)(uint) ( (((uint)buf[7])<<24) | (((uint)buf[6])<<16) | (((uint)buf[5])<<8) | buf[4] );}
            }
            return -1; // ここにはこない
        }
        static int subFuncGetFfOfPrepare(byte[] buf)
        {
            // byte1    byte0     byte3    byte2
            // xxxxxxxx xxxxxxxx  xxxxxxxx xxxffxxx
            if ( buf.Length < 2 ) { return -1; } // invalid
            return ((buf[2]>>3) & 0x03);
        }
        static List<byte> subFuncList12ToRegIds(Instruction inst)
        {
            // byte1    byte0     byte3    byte2
            // xxxxxxxx xxxxxxxL  LLLLLLLL LLLxxxxx
            //                 3  22222222 223
            //                 0  45670123 891
            byte[] buf = inst.Bytes;

            var ret = new List<byte>();
            if( ((buf[3]>>3)&1) == 1 ){ ret.Add(20); }
            if( ((buf[3]>>2)&1) == 1 ){ ret.Add(21); }
            if( ((buf[3]>>1)&1) == 1 ){ ret.Add(22); }
            if( ((buf[3]   )&1) == 1 ){ ret.Add(23); }
            if( ((buf[3]>>7)&1) == 1 ){ ret.Add(24); }
            if( ((buf[3]>>6)&1) == 1 ){ ret.Add(25); }
            if( ((buf[3]>>5)&1) == 1 ){ ret.Add(26); }
            if( ((buf[3]>>4)&1) == 1 ){ ret.Add(27); }
            if( ((buf[2]>>7)&1) == 1 ){ ret.Add(28); }
            if( ((buf[2]>>6)&1) == 1 ){ ret.Add(29); }
            if( ((buf[0]   )&1) == 1 ){ ret.Add(30); }
            if( ((buf[2]>>5)&1) == 1 ){ ret.Add(31); }
            return ret;
        }

        static string DescLdsr(Instruction inst)
        {
            // rrrrr:regID, sssss:selID, RRRRR:reg2
            long reg2index = ParseCharInOpe(inst, 'R');
            long regID = ParseCharInOpe(inst, 'r');
            long selID = ParseCharInOpe(inst, 's');

            string t = inst.Ope.Name;
            t = Regex.Replace(t, @"\bGReg\b",   CpuRegs.GetGRegName(reg2index));
            t = Regex.Replace(t, @"\bSysReg\b", CpuRegs.GetSysRegName(regID,selID));
            return t;
        }

        static string DescStsr(Instruction inst)
        {
            // rrrrr:reg2, sssss:selID, RRRRR:regID
            long regID = ParseCharInOpe(inst, 'R');
            long selID = ParseCharInOpe(inst, 's');
            long reg2index = ParseCharInOpe(inst, 'r');

            string t = inst.Ope.Name;
            t = Regex.Replace(t, @"\bGReg\b",   CpuRegs.GetGRegName(reg2index));
            t = Regex.Replace(t, @"\bSysReg\b", CpuRegs.GetSysRegName(regID,selID));
            return t;
        }

        static string DescTrapVector5(Instruction inst)
        {
            string t = DescReg123(inst);
            t = Regex.Replace(t, @"\bvector5\b", ((uint)ParseCharInOpe(inst, 'v')+0x40).ToString("X02")+"h");
            return t;
        }

        static string DescBcondDisp9(Instruction inst)
        {
            long cond = ParseCharInOpe(inst, 'c');
            string t = inst.Ope.Name;
            t = Regex.Replace(t, @"\bBcond\b", "B" + CondOpeNames[cond] );
            // long disp9 = DescReg123Disp9s(inst);

            return t;
        }

        static string DescBcondDisp17(Instruction inst)
        {
            long cond = ParseCharInOpe(inst, 'c');
            string t = inst.Ope.Name;
            t = Regex.Replace(t, @"\bBcond\b", "B" + CondOpeNames[cond] );
            // long disp17 = DescReg123Disp17s(inst);

            return t;
        }

        static string DescCondReg123Imm5(Instruction inst)
        {
            long cond = ParseCharInOpe(inst, 'c');
            string t = _Core_DescReg123Imm5(inst, true);
            t = Regex.Replace(t, @"\bcccc\b", CondOpeNames[cond] );

            return t;
        }

        // rrrrr111111RRRRR MMMML0001001LLL0
        //  (メモ: 原文はMMMML...LLL ではなく MMMMK...LLL だが、 KLLL を4bitの塊としてみなす処理を簡略化するためMMML...LLLとしている)
        static string DescBins1(Instruction inst) { return _Core_DescBins(inst, true,  true ); }
        static string DescBins2(Instruction inst) { return _Core_DescBins(inst, true,  false); }
        static string DescBins3(Instruction inst) { return _Core_DescBins(inst, false, false); }

        static string _Core_DescBins(Instruction inst, bool msbAdd16, bool lsbAdd16)
        {
            int msb = (int)ParseCharInOpe(inst, 'M') + (msbAdd16 ? 16 : 0);
            int lsb = (int)ParseCharInOpe(inst, 'L') + (lsbAdd16 ? 16 : 0);

            string t = DescReg123(inst);
            t = Regex.Replace(t, @"\bwidth\b", (msb-lsb+1).ToString() );
            t = Regex.Replace(t, @"\bpos\b"  ,         lsb.ToString() );
            return t;
        }


        //----

        static string _Core_DescReg123Imm5(Instruction inst, bool signed)
        {
            long imm =  ParseCharInOpe(inst, 'i');
            if ( signed ) { imm = ExtendSign(imm, 5 - 1); }

            string t = DescReg123(inst);
            t = Regex.Replace(t, @"\bimm5\b", imm.ToString());
            return t;
        }

        static string _Core_DescReg123Imm9(Instruction inst, bool signed)
        {
            long imm =  ParseCharInOpe(inst, 'i') | ( ParseCharInOpe(inst, 'I') << 5 );
            if ( signed ) { imm = ExtendSign(imm, 9 - 1); }

            string t = DescReg123(inst);
            t = Regex.Replace(t, @"\bimm9\b", imm.ToString());
            return t;
        }

        static string _Core_DescReg123Imm16(Instruction inst, bool signed, bool showAsHex)
        {
            long imm =  ParseCharInOpe(inst, 'i');
            if ( signed ) { imm = ExtendSign(imm, 16 - 1); }

            string t = DescReg123(inst);
            string s = ((showAsHex) ? (imm.ToString("X04")+"h") : imm.ToString());
            t = Regex.Replace(t, @"\bimm16\b", s );
            return t;
        }

        static string _Core_DescReg123DispN(Instruction inst, int nDigits, bool signed)
        {
            int bitN;
            long disp =  ParseCharInOpe(inst, 'd', out bitN);
            disp <<= nDigits - bitN;
            if ( signed ) {
                disp = ExtendSign(disp, nDigits - 1);
            }
            disp = (uint)disp;

            string t = DescReg123(inst);
            t = Regex.Replace(t, @"\bdisp" + (nDigits.ToString()) + @"\b", disp.ToString("X08"));
            return t;
        }

        static string _Core_DescReg123Disp16b(Instruction inst, bool signed)
        {
            long disp =  (ParseCharInOpe(inst, 'd')<<1) | ParseCharInOpe(inst, 'b');
            if ( signed ) {
                disp = ExtendSign(disp, 16 - 1);
            }
            disp = (uint)disp;

            string t = DescReg123(inst);
            t = Regex.Replace(t, @"\bdisp16\b", disp.ToString("X08"));
            return t;
        }


        static string _Core_DescReg123Disp23(Instruction inst, bool signed)
        {
            byte[] buf = inst.Bytes;

            int bitN;
            int disp23lower = (int)ParseCharInOpe(inst, 'd', out bitN);
            disp23lower <<= 7 - bitN; // dddddd0 をケアするため
            int disp23upper =  (buf[5]<<8) | buf[4];
            long disp23 = ((disp23upper << 7) | disp23lower);
            if ( signed ) {
                disp23 = ExtendSign(disp23, 23 - 1);
            }

            string t = DescReg123(inst);
            t = Regex.Replace(t, @"\bdisp23\b", ((uint)disp23).ToString("X08")+"h");
            return t;
        }

        //////
        // sub functions

        // 2wordまで読み取り
        static long ParseCharInOpe(Instruction inst, char c)
        {
            int cnt; // dummy
            return ParseCharInOpe(inst, c, out cnt);
        }

        static int GetReg1Id(Instruction inst) { return (int)ParseCharInOpe(inst, 'R'); }
        static int GetReg2Id(Instruction inst) { return (int)ParseCharInOpe(inst, 'r'); }
        static int GetReg3Id(Instruction inst) { return (int)ParseCharInOpe(inst, 'w'); }

        static long GetImm5sValue(Instruction inst) { return ExtendSign(ParseCharInOpe(inst, 'i'), 5 - 1); }
        static long GetImm5uValue(Instruction inst) { return            ParseCharInOpe(inst, 'i')        ; }

        static long GetDisp4uValue    (Instruction inst) { return       ParseCharInOpe(inst, 'd');    }
        static long GetDisp5uEvenValue(Instruction inst) { return       ParseCharInOpe(inst, 'd')<<1; }
        static long GetDisp7uValue    (Instruction inst) { return       ParseCharInOpe(inst, 'd');    }
        static long GetDisp8uEvenValue(Instruction inst) { return       ParseCharInOpe(inst, 'd')<<1; }        
        static long GetDisp8uQuadValue(Instruction inst) { return       ParseCharInOpe(inst, 'd')<<2; }

        static long GetDisp16sValueWithb(Instruction inst)
        {
            long valueSmallD = ParseCharInOpe(inst, 'd');
            long valueSmallB = ParseCharInOpe(inst, 'b');
            return ExtendSign((valueSmallD<<1) | valueSmallB, 16 - 1);
        }

        static long GetDisp16sValue(Instruction inst){ return ExtendSign(ParseCharInOpe(inst, 'd')   ,16 - 1); } // dddd dddd dddd dddd

        static long GetDisp16sEvenValue(Instruction inst){ return ExtendSign(ParseCharInOpe(inst, 'd')<<1,16 - 1); } // dddd dddd dddd ddd(0 or 1)
        static long GetDisp23sEvenValue(Instruction inst)
        {
            byte[] buf = inst.Bytes;
            long valueLargeD = (buf[5] << 8) | buf[4];
            long valueSmallD = ParseCharInOpe(inst, 'd');
            return ExtendSign((valueLargeD<<7) | (valueSmallD<<1), 23 - 1);
        }

        static long GetDisp23sValue(Instruction inst)
        {
            byte[] buf = inst.Bytes;
            long valueLargeD = (buf[5] << 8) | buf[4];
            long valueSmallD = ParseCharInOpe(inst, 'd');
            return ExtendSign((valueLargeD<<7) | valueSmallD, 23 - 1);
        }

        static long GetImm9sValue(Instruction inst)
        {
            long imm =  ParseCharInOpe(inst, 'i') | ( ParseCharInOpe(inst, 'I') << 5 );
            return ExtendSign(imm, 9 - 1);
        }
        static long GetImm9uValue(Instruction inst)
        {
            return (ParseCharInOpe(inst, 'i') | ( ParseCharInOpe(inst, 'I') << 5 ));
        }

        static long GetImm16sValue(Instruction inst){ return ExtendSign(ParseCharInOpe(inst, 'i'),16 - 1); }
        static long GetImm16uValue(Instruction inst){ return            ParseCharInOpe(inst, 'i')        ; }

        static long GetImm32Value(Instruction inst)
        {
            byte[] buf = inst.Bytes;
            long valueLargeI = (buf[5] << 8) | buf[4];
            long valueSmallI = ParseCharInOpe(inst, 'i');
            return (valueLargeI<<16) | valueSmallI;
        }

        static long GetDisp32Value_Jmp(Instruction inst)
        {
            byte[] buf = inst.Bytes;
            long valueLargeD = (buf[5] << 8) | buf[4];
            long valueSmallD = ParseCharInOpe(inst, 'd') << 1; // ddddddddddddddd0
            return (valueLargeD<<16) | valueSmallD;
        }

        static long ParseCharInOpe(Instruction inst, char c, out int cnt)
        {
            OpcodeDef opeDef = inst.Ope;
            byte[] buf = inst.Bytes;

            long t = 0;
            cnt = 0;

            if ( buf.Length < 1 ) {return -1;}
            AccumCharMaskedBits(opeDef.OpeStr1, c, buf[0] , buf[1], ref t, ref cnt);

            if ( opeDef.Size >= 4 ) {
                if ( buf.Length < 3 ) {return -1;}
                AccumCharMaskedBits(opeDef.OpeStr2, c, buf[2] , buf[3], ref t, ref cnt);
            }

            return t;
        }

        static void AccumCharMaskedBits(string opeStr, char c, byte lo, byte hi, ref long t, ref int cnt)
        {
            int tmp = ((int)hi << 8) | lo;
            int bitForCompare = (int)1 << (BitsOfInstWord-1);

            for ( int i=0 ; i<BitsOfInstWord ; i++ ) {
                if ( opeStr[i] == c ) {
                    t <<= 1;
                    if ( (tmp & bitForCompare) != 0 ) {
                        t |= 1;
                    }
                    cnt++;
                }
                bitForCompare >>= 1;
            }
        }


        public OpcodeDef(string _name,
                         bool _hasJump, AbsJumpCalcFunc _arg_absJumpCalcFunc,
                         string _opeStr1, string _opeStr2, FormatId _fmt,
                         DescFunc _arg_descFunc)
        {
            Name    = _name;
            HasJump = _hasJump;
            _absJumpCalcFunc = _arg_absJumpCalcFunc;
            OpeStr1 = _opeStr1;
            OpeStr2 = _opeStr2;
            Fmt     = _fmt;
            _descFunc = _arg_descFunc;

            // 一貫性チェック
            if ( OpeStr1.Length != 16 ) {
                throw new Exception("Bug opcode length unmatch#1");
            }
            // 2 byte 命令
            if ( Size == 2 ) {
                if ( OpeStr2 != null ) {
                    throw new Exception("Bug opcode Fmt unmatch#1");
                }

                try{
                    // opcode部分が0,1の数字であることをチェックする
                    if ( Fmt == FormatId.I  ||
                         Fmt == FormatId.IIa  ) {
                        Convert.ToInt32(OpeStr1.Substring(5, 6), 2); // index 5 から 6文字(bit10..5)
                    }
                    else if ( Fmt == FormatId.III ||
                              Fmt == FormatId.IVa   ) {
                        Convert.ToInt32(OpeStr1.Substring(5, 4), 2); // index 5 から 4文字(bit10..7)
                    }
                    else if ( Fmt == FormatId.IVb ) {
                        Convert.ToInt32(OpeStr1.Substring(5, 7), 2); // index 5 から 7文字(bit10..4)
                    }
                    else { // check対象外
                    }
                }
                catch (Exception) {
                    throw new Exception("Bug opcode Fmt unmatch:" + Name + ", " + OpeStr1 );
                }
            }
            else if ( Size == 4 ) {
                if ( OpeStr2 == null || OpeStr2.Length != 16 ) {
                    throw new Exception("Bug opcode2 unmatch#2");
                }
            }
            else { // check対象外
            }
        }

        public bool IsMatch(byte[] buf, long offsetI)
        {
            long size = Size;

            if ( buf == null ) {
                throw new Exception("OpcodeDef.IsMatch(null, offsetI="+offsetI.ToString()+") is called.");
            }

            if ( buf.Length - offsetI < size ) {
                return false;
            }

            if ( size == 2 ) {
                return IsMatchSubSeq(buf[offsetI], buf[offsetI+1], OpeStr1);
            }
            else { // 4 or 6 or 8
                return IsMatchSubSeq(buf[offsetI  ], buf[offsetI+1], OpeStr1)
                    && IsMatchSubSeq(buf[offsetI+2], buf[offsetI+3], OpeStr2);
            }
        }

        bool IsMatchSubSeq(byte lo, byte hi, string s)
        {
            int tmp = ((int)hi << 8) | lo;
            int bitForCompare = (int)1 << (BitsOfInstWord-1);
            for (int pos=0 ; pos<BitsOfInstWord ; pos++) {
                if ( s[pos] == '0' ) {
                    if ( (tmp & bitForCompare) != 0 ) {
                        return false;
                    }
                }
                else if ( s[pos] == '1' ) {
                    if ( (tmp & bitForCompare) == 0 ) {
                        return false;
                    }
                }
                bitForCompare >>= 1;
            }
            return true;
        }

        public static readonly OpcodeDef[] Opcodes = new OpcodeDef[]{
            // 固定値を優先的にマッチングさせる
            // 左の桁から  0->1->don't care  の順で並べる

            // メモ: "!"から始まっているものは未実装

            // G3KH は little endian (byte 0が下位データ)
            //                                                              byte1,0           byte3,2
            new OpcodeDef("Nop"                          ,false ,DummyFunc,"0000000000000000",null              ,FormatId.I    ,DescText             ),
            new OpcodeDef("Synci"                        ,false ,DummyFunc,"0000000000011100",null              ,FormatId.I    ,DescText             ),
            new OpcodeDef("Synce"                        ,false ,DummyFunc,"0000000000011101",null              ,FormatId.I    ,DescText             ),
            new OpcodeDef("Syncm"                        ,false ,DummyFunc,"0000000000011110",null              ,FormatId.I    ,DescText             ),
            new OpcodeDef("Syncp"                        ,false ,DummyFunc,"0000000000011111",null              ,FormatId.I    ,DescText             ),
            new OpcodeDef("Rie"                          ,false ,DummyFunc,"0000000001000000",null              ,FormatId.I    ,DescText             ),
            new OpcodeDef("Switch reg1"                  ,false ,DummyFunc,"00000000010RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Jmp [reg1]"                   ,true  ,DummyFunc,"00000000011RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Zxb reg1"                     ,false ,DummyFunc,"00000000100RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Sxb reg1"                     ,false ,DummyFunc,"00000000101RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Zxh reg1"                     ,false ,DummyFunc,"00000000110RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Sxh reg1"                     ,false ,DummyFunc,"00000000111RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("!Callt imm6"                  ,false ,DummyFunc,"0000001000iiiiii",null              ,FormatId.IIb  ,DescText             ), // マニュアルが仕様矛盾している...
            new OpcodeDef("Jr disp32"                    ,true  ,RelJmp32, "0000001011100000","ddddddddddddddd0",FormatId.VIb  ,DescText             ), // 3rd word
            new OpcodeDef("Jarl disp32,reg1"             ,true  ,RelJmp32, "00000010111RRRRR","ddddddddddddddd0",FormatId.VIb  ,DescReg123           ), // 3rd word
            new OpcodeDef("Mov imm32,reg1"               ,false ,DummyFunc,"00000110001RRRRR","iiiiiiiiiiiiiiii",FormatId.VIb  ,DescReg123Imm32      ), // 3rd word
            new OpcodeDef("Dispose imm5,list12"          ,false ,DummyFunc,"0000011001iiiiiL","LLLLLLLLLLL00000",FormatId.XIIIa,DescDispose          ),
            new OpcodeDef("Dispose imm5,list12,[reg1]"   ,false ,DummyFunc,"0000011001iiiiiL","LLLLLLLLLLLRRRRR",FormatId.XIIIa,DescDispose          ),
            new OpcodeDef("Prepare list12,imm5"          ,false ,DummyFunc,"0000011110iiiiiL","LLLLLLLLLLL00001",FormatId.XIIIa,DescPrepare2word     ),
            new OpcodeDef("Prepare list12,imm5,ep:=sp"   ,false ,DummyFunc,"0000011110iiiiiL","LLLLLLLLLLL00011",FormatId.XIIIa,DescPrepare2word     ),
            new OpcodeDef("Prepare list12,imm5,ep:=imm32",false ,DummyFunc,"0000011110iiiiiL","LLLLLLLLLLL11011",FormatId.XIIIc,DescPrepare_Imm32    ), // 4th word
            new OpcodeDef("Prepare list12,imm5,ep:=imm16",false ,DummyFunc,"0000011110iiiiiL","LLLLLLLLLLLff011",FormatId.XIIIb,DescPrepare_Imm16    ), // 3rd word
            new OpcodeDef("Jmp disp32 [reg1]"            ,true  ,DummyFunc,"00000110111RRRRR","ddddddddddddddd0",FormatId.VIb  ,DescReg123           ), // 3rd word
            new OpcodeDef("Loop reg1,disp16"             ,true  ,RelLoop,  "00000110111RRRRR","ddddddddddddddd1",FormatId.VII  ,DescReg123           ),
            new OpcodeDef("Ld.h disp23 [reg1],reg3"      ,false ,DummyFunc,"00000111100RRRRR","wwwwwdddddd00111",FormatId.XIVb ,DescReg123Disp23s    ), // 3rd word
            new OpcodeDef("Ld.w disp23 [reg1],reg3"      ,false ,DummyFunc,"00000111100RRRRR","wwwwwdddddd01001",FormatId.XIVb ,DescReg123Disp23s    ), // 3rd word
            new OpcodeDef("St.w reg3,disp23 [reg1]"      ,false ,DummyFunc,"00000111100RRRRR","wwwwwdddddd01111",FormatId.XIVb ,DescReg123Disp23s    ), // 3rd word
            new OpcodeDef("Ld.b disp23 [reg1],reg3"      ,false ,DummyFunc,"00000111100RRRRR","wwwwwddddddd0101",FormatId.XIVb ,DescReg123Disp23s    ), // 3rd word
            new OpcodeDef("St.b reg3,disp23 [reg1]"      ,false ,DummyFunc,"00000111100RRRRR","wwwwwddddddd1101",FormatId.XIVb ,DescReg123Disp23s    ), // 3rd word
            new OpcodeDef("St.h reg3,disp23 [reg1]"      ,false ,DummyFunc,"00000111101RRRRR","wwwwwdddddd01101",FormatId.XIVb ,DescReg123Disp23s    ), // 3rd word
            new OpcodeDef("Ld.hu disp23 [reg1],reg3"     ,false ,DummyFunc,"00000111101RRRRR","wwwwwdddddd00111",FormatId.XIVb ,DescReg123Disp23u    ), // 3rd word
            new OpcodeDef("Ld.dw disp23 [reg1],reg3"     ,false ,DummyFunc,"00000111101RRRRR","wwwwwdddddd01001",FormatId.XIVb ,DescReg123Disp23s    ), // 3rd word
            new OpcodeDef("St.dw reg3,disp23 [reg1]"     ,false ,DummyFunc,"00000111101RRRRR","wwwwwdddddd01111",FormatId.XIVb ,DescReg123Disp23s    ), // 3rd word
            new OpcodeDef("Ld.bu disp23 [reg1],reg3"     ,false ,DummyFunc,"00000111101RRRRR","wwwwwddddddd0101",FormatId.XIVb ,DescReg123Disp23u    ), // 3rd word
            new OpcodeDef("Jr disp22"                    ,true  ,RelJmp,   "0000011110dddddd","ddddddddddddddd0",FormatId.V    ,DescText             ),
            new OpcodeDef("Halt"                         ,false ,DummyFunc,"0000011111100000","0000000100100000",FormatId.X    ,DescText             ),
            new OpcodeDef("Ctret"                        ,false ,DummyFunc,"0000011111100000","0000000101000100",FormatId.X    ,DescText             ),
            new OpcodeDef("Eiret"                        ,false ,DummyFunc,"0000011111100000","0000000101001000",FormatId.X    ,DescText             ),
            new OpcodeDef("Feret"                        ,false ,DummyFunc,"0000011111100000","0000000101001010",FormatId.X    ,DescText             ),
            new OpcodeDef("Di"                           ,false ,DummyFunc,"0000011111100000","0000000101100000",FormatId.X    ,DescText             ),
            new OpcodeDef("Trap vector5"                 ,false ,DummyFunc,"00000111111vvvvv","0000000100000000",FormatId.X    ,DescTrapVector5      ),
            new OpcodeDef("Bcond disp17"                 ,true  ,RelJmp,   "00000111111dcccc","ddddddddddddddd1",FormatId.VII  ,DescBcondDisp17      ),
            new OpcodeDef("!Ldl.w disp16 [reg1],reg3"    ,false ,DummyFunc,"00000111111RRRRR","wwwww01101111000",FormatId.VII  ,DescText             ),
            new OpcodeDef("Stc.w reg3, [reg1]"           ,false ,DummyFunc,"00000111111RRRRR","wwwww01101111010",FormatId.VII  ,DescReg123           ),
            new OpcodeDef("Snooze"                       ,false ,DummyFunc,"0000111111100000","0000000100100000",FormatId.X    ,DescText             ),
            new OpcodeDef("Set1 bit#3,disp16[reg1]"      ,false ,DummyFunc,"00bbb111110RRRRR","dddddddddddddddd",FormatId.VIII ,DescReg123Bit3Disp16 ),
            new OpcodeDef("!Pushsp rh-rt"                ,false ,DummyFunc,"01000111111RRRRR","wwwww00101100000",FormatId.XI   ,DescText             ),
            new OpcodeDef("!Popsp rh-rt"                 ,false ,DummyFunc,"01100111111RRRRR","wwwww00101100000",FormatId.XI   ,DescText             ),
            new OpcodeDef("Not1 bit#3,disp16[reg1]"      ,false ,DummyFunc,"01bbb111110RRRRR","dddddddddddddddd",FormatId.VIII ,DescReg123Bit3Disp16 ),
            new OpcodeDef("!Fetrap vector4"              ,false ,DummyFunc,"0vvvv00001000000",null              ,FormatId.I    ,DescText             ),
            new OpcodeDef("Ei"                           ,false ,DummyFunc,"1000011111100000","0000000101100000",FormatId.X    ,DescText             ),
            new OpcodeDef("Clr1 bit#3,disp16[reg1]"      ,false ,DummyFunc,"10bbb111110RRRRR","dddddddddddddddd",FormatId.VIII ,DescReg123Bit3Disp16 ),
            new OpcodeDef("Jarl [reg1],reg3"             ,true  ,DummyFunc,"11000111111RRRRR","wwwww00101100000",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("!Syscall vector8"             ,false ,DummyFunc,"11010111111vvvvv","00VVV00101100000",FormatId.X    ,DescText             ),
            new OpcodeDef("Cll"                          ,false ,DummyFunc,"1111111111111111","1111000101100000",FormatId.X    ,DescText             ),
            new OpcodeDef("Tst1 bit#3,disp16[reg1]"      ,false ,DummyFunc,"11bbb111110RRRRR","dddddddddddddddd",FormatId.VIII ,DescReg123Bit3Disp16 ),
            new OpcodeDef("Mov reg1,reg2"                ,false ,DummyFunc,"rrrrr000000RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Not reg1,reg2"                ,false ,DummyFunc,"rrrrr000001RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("?Divh reg1,reg2"              ,false ,DummyFunc,"rrrrr000010RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Sld.bu disp4 [ep],reg2"       ,false ,DummyFunc,"rrrrr0000110dddd",null              ,FormatId.IVb  ,DescReg123Disp4u     ),
            new OpcodeDef("Sld.hu disp5 [ep],reg2"       ,false ,DummyFunc,"rrrrr0000111dddd",null              ,FormatId.IVb  ,DescReg123Disp5u     ),
            new OpcodeDef("Satsubr reg1,reg2"            ,false ,DummyFunc,"rrrrr000100RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Satsub reg1,reg2"             ,false ,DummyFunc,"rrrrr000101RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Satadd reg1,reg2"             ,false ,DummyFunc,"rrrrr000110RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Mulh reg1,reg2"               ,false ,DummyFunc,"rrrrr000111RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Or reg1,reg2"                 ,false ,DummyFunc,"rrrrr001000RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Xor reg1,reg2"                ,false ,DummyFunc,"rrrrr001001RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("And reg1,reg2"                ,false ,DummyFunc,"rrrrr001010RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Tst reg1,reg2"                ,false ,DummyFunc,"rrrrr001011RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Subr reg1,reg2"               ,false ,DummyFunc,"rrrrr001100RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Sub reg1,reg2"                ,false ,DummyFunc,"rrrrr001101RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Add reg1,reg2"                ,false ,DummyFunc,"rrrrr001110RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Cmp reg1,reg2"                ,false ,DummyFunc,"rrrrr001111RRRRR",null              ,FormatId.I    ,DescReg123           ),
            new OpcodeDef("Mov imm5,reg2"                ,false ,DummyFunc,"rrrrr010000iiiii",null              ,FormatId.IIa  ,DescReg123Imm5s      ),
            new OpcodeDef("Satadd imm5,reg2"             ,false ,DummyFunc,"rrrrr010001iiiii",null              ,FormatId.IIa  ,DescReg123Imm5s      ),
            new OpcodeDef("Add imm5,reg2"                ,false ,DummyFunc,"rrrrr010010iiiii",null              ,FormatId.IIa  ,DescReg123Imm5s      ),
            new OpcodeDef("Cmp imm5,reg2"                ,false ,DummyFunc,"rrrrr010011iiiii",null              ,FormatId.IIa  ,DescReg123Imm5s      ),
            new OpcodeDef("Shr imm5,reg2"                ,false ,DummyFunc,"rrrrr010100iiiii",null              ,FormatId.IIa  ,DescReg123Imm5u      ),
            new OpcodeDef("Sar imm5,reg2"                ,false ,DummyFunc,"rrrrr010101iiiii",null              ,FormatId.IIa  ,DescReg123Imm5u      ),
            new OpcodeDef("Shl imm5,reg2"                ,false ,DummyFunc,"rrrrr010110iiiii",null              ,FormatId.IIa  ,DescReg123Imm5u      ),
            new OpcodeDef("Mulh imm5,reg2"               ,false ,DummyFunc,"rrrrr010111iiiii",null              ,FormatId.IIa  ,DescReg123Imm5s      ),
            new OpcodeDef("Sld.b disp7 [ep],reg2"        ,false ,DummyFunc,"rrrrr0110ddddddd",null              ,FormatId.IVa  ,DescReg123Disp7u     ),
            new OpcodeDef("Sst.b reg2,disp7[ep]"         ,false ,DummyFunc,"rrrrr0111ddddddd",null              ,FormatId.IVa  ,DescReg123Disp7u     ),
            new OpcodeDef("Sld.h disp8 [ep],reg2"        ,false ,DummyFunc,"rrrrr1000ddddddd",null              ,FormatId.IVa  ,DescReg123Disp8u     ),
            new OpcodeDef("Sst.h reg2,disp8[ep]"         ,false ,DummyFunc,"rrrrr1001ddddddd",null              ,FormatId.IVa  ,DescReg123Disp8u     ),
            new OpcodeDef("Sld.w disp8 [ep],reg2"        ,false ,DummyFunc,"rrrrr1010dddddd0",null              ,FormatId.IVa  ,DescReg123Disp8u     ),
            new OpcodeDef("Sst.w reg2,disp8[ep]"         ,false ,DummyFunc,"rrrrr1010dddddd1",null              ,FormatId.IVa  ,DescReg123Disp8u     ),
            new OpcodeDef("Bcond disp9"                  ,true  ,RelJmp,   "ddddd1011dddcccc",null              ,FormatId.III  ,DescBcondDisp9       ),
            new OpcodeDef("Addi imm16,reg1,reg2"         ,false ,DummyFunc,"rrrrr110000RRRRR","iiiiiiiiiiiiiiii",FormatId.VIa  ,DescReg123Imm16s     ),
            new OpcodeDef("Movea imm16,reg1,reg2"        ,false ,DummyFunc,"rrrrr110001RRRRR","iiiiiiiiiiiiiiii",FormatId.VIa  ,DescReg123Imm16s     ),
            new OpcodeDef("Movhi imm16,reg1,reg2"        ,false ,DummyFunc,"rrrrr110010RRRRR","iiiiiiiiiiiiiiii",FormatId.VIa  ,DescReg123Imm16u     ),
            new OpcodeDef("!Satsubi imm16,reg1,reg2"     ,false ,DummyFunc,"rrrrr110011RRRRR","iiiiiiiiiiiiiiii",FormatId.VIa  ,DescReg123Imm16s     ),
            new OpcodeDef("Ori imm16,reg1,reg2"          ,false ,DummyFunc,"rrrrr110100RRRRR","iiiiiiiiiiiiiiii",FormatId.VIa  ,DescReg123Imm16uHex  ),
            new OpcodeDef("Xori imm16,reg1,reg2"         ,false ,DummyFunc,"rrrrr110101RRRRR","iiiiiiiiiiiiiiii",FormatId.VIa  ,DescReg123Imm16uHex  ),
            new OpcodeDef("Andi imm16,reg1,reg2"         ,false ,DummyFunc,"rrrrr110110RRRRR","iiiiiiiiiiiiiiii",FormatId.VIa  ,DescReg123Imm16uHex  ),
            new OpcodeDef("Mulhi imm16,reg1,reg2"        ,false ,DummyFunc,"rrrrr110111RRRRR","iiiiiiiiiiiiiiii",FormatId.VIa  ,DescReg123Imm16u     ),
            new OpcodeDef("Ld.b disp16 [reg1],reg2"      ,false ,DummyFunc,"rrrrr111000RRRRR","dddddddddddddddd",FormatId.VII  ,DescReg123Disp16s    ),
            new OpcodeDef("Ld.h disp16 [reg1],reg2"      ,false ,DummyFunc,"rrrrr111001RRRRR","ddddddddddddddd0",FormatId.VII  ,DescReg123Disp16s    ),
            new OpcodeDef("Ld.w disp16 [reg1],reg2"      ,false ,DummyFunc,"rrrrr111001RRRRR","ddddddddddddddd1",FormatId.VII  ,DescReg123Disp16s    ),
            new OpcodeDef("St.b reg2,disp16 [reg1]"      ,false ,DummyFunc,"rrrrr111010RRRRR","dddddddddddddddd",FormatId.VII  ,DescReg123Disp16s    ),
            new OpcodeDef("St.h reg2,disp16 [reg1]"      ,false ,DummyFunc,"rrrrr111011RRRRR","ddddddddddddddd0",FormatId.VII  ,DescReg123Disp16s    ),
            new OpcodeDef("St.w reg2,disp16 [reg1]"      ,false ,DummyFunc,"rrrrr111011RRRRR","ddddddddddddddd1",FormatId.VII  ,DescReg123Disp16s    ),
            new OpcodeDef("Jarl disp22,reg2"             ,true  ,RelJmp,   "rrrrr11110dddddd","ddddddddddddddd0",FormatId.V    ,DescReg123           ), // 仮desc
            new OpcodeDef("Ld.bu disp16 [reg1],reg2"     ,false ,DummyFunc,"rrrrr11110bRRRRR","ddddddddddddddd1",FormatId.VII  ,DescReg123Disp16sB   ),
            new OpcodeDef("Bsw reg2,reg3"                ,false ,DummyFunc,"rrrrr11111100000","wwwww01101000000",FormatId.XII  ,DescReg123           ),
            new OpcodeDef("Bsh reg2,reg3"                ,false ,DummyFunc,"rrrrr11111100000","wwwww01101000010",FormatId.XII  ,DescReg123           ),
            new OpcodeDef("Hsw reg2,reg3"                ,false ,DummyFunc,"rrrrr11111100000","wwwww01101000100",FormatId.XII  ,DescReg123           ),
            new OpcodeDef("Hsh reg2,reg3"                ,false ,DummyFunc,"rrrrr11111100000","wwwww01101000110",FormatId.XII  ,DescReg123           ),
            new OpcodeDef("sch0r reg2,reg3"              ,false ,DummyFunc,"rrrrr11111100000","wwwww01101100000",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("sch1r reg2,reg3"              ,false ,DummyFunc,"rrrrr11111100000","wwwww01101100010",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("sch0l reg2,reg3"              ,false ,DummyFunc,"rrrrr11111100000","wwwww01101100100",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("sch1l reg2,reg3"              ,false ,DummyFunc,"rrrrr11111100000","wwwww01101100110",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("Setf cccc reg2"               ,false ,DummyFunc,"rrrrr1111110cccc","0000000000000000",FormatId.IX   ,DescCondReg123Imm5   ),
            new OpcodeDef("Sasf cccc,reg2"               ,false ,DummyFunc,"rrrrr1111110cccc","0000001000000000",FormatId.IX   ,DescCondReg123Imm5   ),
            new OpcodeDef("!Rie imm5,imm4"               ,false ,DummyFunc,"iiiii1111111IIII","0000000000000000",FormatId.X    ,DescText             ),
            new OpcodeDef("Shr reg1,reg2"                ,false ,DummyFunc,"rrrrr111111RRRRR","0000000010000000",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("Sar reg1,reg2"                ,false ,DummyFunc,"rrrrr111111RRRRR","0000000010100000",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("Shl reg1,reg2"                ,false ,DummyFunc,"rrrrr111111RRRRR","0000000011000000",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("Set1 reg2,[reg1]"             ,false ,DummyFunc,"rrrrr111111RRRRR","0000000011100000",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("Not1 reg2,[reg1]"             ,false ,DummyFunc,"rrrrr111111RRRRR","0000000011100010",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("Clr1 reg2,[reg1]"             ,false ,DummyFunc,"rrrrr111111RRRRR","0000000011100100",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("Tst1 reg2,[reg1]"             ,false ,DummyFunc,"rrrrr111111RRRRR","0000000011100110",FormatId.IX   ,DescReg123           ),
            new OpcodeDef("!Mac reg1,reg2,reg3,reg4"     ,false ,DummyFunc,"rrrrr111111RRRRR","wwww0011110mmmm0",FormatId.XI   ,DescText             ),
            new OpcodeDef("!Macu reg1,reg2,reg3,reg4"    ,false ,DummyFunc,"rrrrr111111RRRRR","wwww0011111mmmm0",FormatId.XI   ,DescText             ),
            new OpcodeDef("Ldsr GReg,SysReg"             ,false ,DummyFunc,"rrrrr111111RRRRR","sssss00000100000",FormatId.IX   ,DescLdsr             ),
            new OpcodeDef("Stsr SysReg,GReg"             ,false ,DummyFunc,"rrrrr111111RRRRR","sssss00001000000",FormatId.IX   ,DescStsr             ),
            new OpcodeDef("Shr reg1,reg2,reg3"           ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww00010000010",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Bins reg1,pos,width,reg2"     ,false ,DummyFunc,"rrrrr111111RRRRR","MMMML0001001LLL0",FormatId.IX   ,DescBins1            ),
            new OpcodeDef("Sar reg1,reg2,reg3"           ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww00010100010",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Bins reg1,pos,width,reg2"     ,false ,DummyFunc,"rrrrr111111RRRRR","MMMML0001011LLL0",FormatId.IX   ,DescBins2            ),
            new OpcodeDef("Shl reg1,reg2,reg3"           ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww00011000010",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Rotl imm5,reg2,reg3"          ,false ,DummyFunc,"rrrrr111111iiiii","wwwww00011000100",FormatId.XII  ,DescReg123Imm5u      ),
            new OpcodeDef("Bins reg1,pos,width,reg2"     ,false ,DummyFunc,"rrrrr111111RRRRR","MMMML0001101LLL0",FormatId.IX   ,DescBins3            ),
            new OpcodeDef("Caxi [reg1],reg2,reg3"        ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww00011101110",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Mul reg1,reg2,reg3"           ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01000100000",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Mulu reg1,reg2,reg3"          ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01000100010",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Mul imm9,reg2,reg3"           ,false ,DummyFunc,"rrrrr111111iiiii","wwwww01001IIII00",FormatId.XII  ,DescReg123Imm9s      ),
            new OpcodeDef("Mulu imm9,reg2,reg3"          ,false ,DummyFunc,"rrrrr111111iiiii","wwwww01001IIII10",FormatId.XII  ,DescReg123Imm9u      ),
            new OpcodeDef("Divh reg1,reg2,reg3"          ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01010000000",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Divhu reg1,reg2,reg3"         ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01010000010",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Div reg1,reg2,reg3"           ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01011000000",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Divu reg1,reg2,reg3"          ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01011000010",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Divq reg1,reg2,reg3"          ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01011111100",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Divqu reg1,reg2,reg3"         ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01011111110",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Cmov(cccc) imm5,reg2,reg3"    ,false ,DummyFunc,"rrrrr111111iiiii","wwwww011000cccc0",FormatId.XII  ,DescCondReg123Imm5   ),
            new OpcodeDef("Cmov(cccc) reg1,reg2,reg3"    ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww011001cccc0",FormatId.XI   ,DescCondReg123Imm5   ),
            new OpcodeDef("Satsub reg1,reg2,reg3"        ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01110011010",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Sbf(cccc) reg1,reg2,reg3"     ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww011100cccc0",FormatId.XI   ,DescCondReg123Imm5   ),
            new OpcodeDef("Satadd reg1,reg2,reg3"        ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww01110111010",FormatId.XI   ,DescReg123           ),
            new OpcodeDef("Adf(cccc) reg1,reg2,reg3"     ,false ,DummyFunc,"rrrrr111111RRRRR","wwwww011101cccc0",FormatId.XI   ,DescCondReg123Imm5   ),
            new OpcodeDef("Ld.hu disp16 [reg1],reg2"     ,false ,DummyFunc,"rrrrr111111RRRRR","ddddddddddddddd1",FormatId.VII  ,DescReg123Disp16s    ),
        };
    }

    static readonly string[] CondOpeNames = new string[]{
        "v" , "l" ,"e" ,"nh",
        "n" ,"r" ,"lt","le",
        "nv","nl","f" ,"h" ,
        "p" ,"sa","ge","gt",
    };
}

参考サイト

RH850G3KH
ユーザーズマニュアル ソフトウェア編 Rev.1.20 2016.12 - ルネサス エレクトロニクス

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# 例外処理(Try-Catch)がないところで出るエラーを全て一纏めにキャッチする

C#でtry-catchが記載されていない箇所でエラーが起きても
アプリが落ちないようにする。

C#では、
「Application.ThreadException」イベントが
用意されている。

使い方
Program.cs(コピペ用)

static class Program
{
    /// <summary>
    /// アプリケーションのメイン エントリ ポイントです。
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.ThreadException += Application_ThreadException;

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        MessageBox.Show(e.Exception.Message);
    }
}

画像
キャプチャ.PNG

参考資料:
■適切に処理されなかった例外をキャッチするには?
 https://www.atmarkit.co.jp/ait/articles/0507/01/news134.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

匿名型とタプル

長い間、匿名型とタプルを同じものだと思い込んでいたため、反省と備忘録をこめて記事を作成しました。

匿名型

List<string> strList = new List<string>() { "AAA", "BBB", "CCC", "DDD", "EEE"};

//anoはタプルではない!匿名型
var ano = strList.Select((str, index) => new { str, index });

上記のような記載をした場合、anoはタプルのリストではなく匿名型のリストとなります。
そのため、以下のような代入はできません。

//↓NG : '<anonymous type: string str, int index>'を'(string str, int index)'に暗黙的に変換できません
//(string str, int index) val = ano.FirstOrDefault();

タプル

List<string> strList = new List<string>() { "AAA", "BBB", "CCC", "DDD", "EEE"};

//tupはタプル!
var tup = strList.Select((str, index) => (str, index));

一方こちらは、タプルのリストとなります。
以下のような代入が可能です。

//↓OK
(string str, int index) val = tup.FirstOrDefault();

補足

本記事ではC#8.0から標準で導入されているValueTupleのことをタプルと呼んでいます。
Visual Studio 2017を使用されている場合、NuGetからValueTupleパッケージをインストールすることで利用可能です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Visual Studio要らずで始める C# ~ Windows向けお手軽ツール開発 (かきかけ)

はじめに

プログラミングの上達への近道は、「とにかく作りたいものを決めてやってみる」に限る。

C#は、Windows7以降であれば標準でコンパイル環境が入っているので、テキストエディタさえあればすぐに始められる。
(「環境構築しようとしてパッケージ間のバージョンが合わない」というようなことで挫折する目に遭わずに済みます。)

追記:Excelマクロやバッチファイル以外の選択肢として、C#は強力なツールになると思います。(※適材適所ではあるので、使い分けが大事。)

追記

開発環境(Visual Studio)については、導入可能であれば、慣れのためにも入れたほうがよいです(外部ライブラリとかも取り込みやすいし、キーワード補完とかも便利)。

職場的に環境を入れれない場合は本記事の内容が参考になるかと思います。

とりあえずはじめよう

コマンドプロンプト(cmd.exe)を使います。この時点で不安がある人は、超入門 - 知識ゼロからコマンドプロンプトをつかう - Qiitaを見てね!

csc.exeでC#のソースコードをインプットとしてコンパイルします。
既に解説記事がたくさんあるので、調べてやってみてください。(核心部分を丸投げ。)
下記の記事が参考になるかと思います。

(※PowerShellである必要はなく、コマンドプロンプトで代用できます。)

上記でうまくいかない場合は、下記から探すかググるかして頑張ってください・・・。

初心者が心掛けるべきこと

  • 超重要 (身を守るために)
    • 職場などの本番環境で危ないこと1をやってはいけない。ファイル操作やネットワーク通信など。どんなことになるかは 本番環境でやらかしちゃった人 Advent Calendar 2019 - Qiita とかを見てみるのもよいかも。
    • 機密情報の管理に注意する。(とくに、ソースコードや入力用のテキストファイルにパスワードを書いたりする場合は、うっかり漏洩しないように注意。)
    • 職場のルールを守る。(ツールやライブラリの、使用やインストールなど。)
    • 自作ソフトがウィルスとして誤検知されることがあるので、その場合の対応についてあらかじめ考慮しておく2。(投げやり・・・)
    • 自作ソフトを使って業務や製品開発している場合に、自作ツールの不備が一要因となってトラブルや不具合混入や不具合見逃しなどを引き起すケースもあり得るので、無保証であることを全面的に周知しつつエビデンスを残すなりウィンドウのタイトルの先頭に【無保証】とつけるなり色々と保身を図ったほうがよい。
    • 他人の書いたソースコードやライブラリなどをローカル環境以外で使うケース(社内外に公開したり商用利用したりなど)では、著作権など国内外の法律に抵触しないかに注意する。
  • 開発環境(使用ツール)
    • 使いやすいテキストエディタを導入する。Undo&Redo,シンタックスハイライト,検索&置換は必須。Visual Studio Codeがベストだと思うが、サクラエディタとかでもキーワード設定とかしっかりやっておけば十分何とか使える3。メモ帳は無理。。
    • バージョン管理ツールをつかってソースコードのバージョン管理をする。4
    • テキスト差分比較ツールをいれておくとよい。
  • Windowsプログラミング(≒ツールづくり)で意識していること
    • 自動化しすぎない。汎用化しすぎない。(投入労力と有用性のトレードオフのポイントを定める。汎用的に作ろうとするとケアすべきことが爆発的に増えるので頓挫する。)
    • 独自色を出さない。(マニュアルが無くてもすんなり使えるのが理想)
    • 時間軸を意識する。(例. 途中で編集対象のファイル内容が変更されているかもしれない)
    • 変わりそうなところ(機能を追加したりするつもりのところ)を予測して、取り換えが利くように関数(メソッド)を分離するなどする。
    • 特定の種類のファイルを扱うとき - ファイルフォーマットの規格を調べる。
    • 特定の種類のファイルを扱うとき - 規格を守っていないデファクトスタンダードにも気を付ける5
  • あるものは使うべし
    • ありそうな機能は、標準ライブラリにあったり、誰かがソースを公開してたりするので、それを組み合わせて使うのが基本。6
  • 調べかた
    • わからないことはまずは調べる。
    • 堅苦しい本やリファレンス本を買うよりもネットで調べるべし。(「習うより慣れろ」なのと、この分野の本の情報(とくに特定の言語に特化したもの)はすぐ陳腐化する。)
    • ただしネットの情報は鵜呑みにしてはいけない。(バグってるコードとか、非推奨なコードが平気で転がっている。)
    • 掲示板とかブログなどの単発記事よりも、++c++;dobonなど、まとまっていて論理立った説明を多数書いてくれているサイトのものを採用するのがよい。(ただ、ニッチな内容を調べるときは掲示板とかを頼らざるを得ない場合もある。)
    • Qiitaの記事の信頼性については、各記事や著者次第なところがある。情報発信ツールであり、ノウハウ共有のためのツールであって、内容が常に正しいわけではない。自分自身もあまり裏どりせずに記事を書いてたり、「とりあえず動けばいいや」レベルのコードを投稿してたりする。
  • 文法知識
    • 変数の型とか、関数(メソッド)とか、制御構文(条件分岐、繰り返し)の書き方は、トライアンドエラーする前に、多少は理解してから着手したほうがよい。少なくとも代入文とかオーバーフローを知らずにコーディングし始めるのはNGだと思う。
    • とはいえ手を動かさないと始まらないので、まずはHello world的な入門コードをコピペって動作させるところから。
  • コーディング
    • インデントする。これ絶対! 参考: インデントをしよう!(プログラミング)
    • 極力コーディングスタイルを決めて統一する(市民権のあるスタイルからいずれかを採用しておいたほうがよい)。括弧とか空白,改行の位置、変数の大文字小文字など。
    • 判りやすい名前をつける。 hoge1 hoge2とかはヤメテ!
    • ソースにつけるコメントは必要十分に。7
    • 機能単位で関数(メソッド)単位に分けていく。
      イメージ:「〇〇を△△から検索する機能」「〇〇を入力として△△を表示する機能」
    • 慣れてきたら class を分解する。無理して使うのではなく、使うと楽になると思ったら使うのがよいかと。(感覚的には、1000行超えたあたりから、分解しないと管理できなくなってくる。)
  • コンパイル
  • 動作テスト
    • 細かい単位に分けて想定通りの動作をするかを試すのがよい。わりとビッグバンテスト的にやってしまいがちだが、不具合箇所を突き止めるのが大変。
  • 情報工学とか数学とか
    • プロとしてやっていくつもりなら、避けては通れない。
    • プログラムはなぜ動くのか とかの本を読んでみるのがよい(?)
    • メモリのアドレス空間とか、整数や浮動小数点数がデータがビット(0,1)の並びとしてどう表現されているかとか、原理的なことを知ると、色々なことがクリアに理解できるようになる。
    • 大規模なデータを扱うときは、アルゴリズムに関する知識が必要になってくる。(処理時間とかメモリリソースを意識する場面)
    • 音声処理や3Dグラフィックスや機械学習8などを取り扱う場合は、高校~大学レベルの数学の概念や計算規則をしっかり理解していないと厳しい。
  • 文字コードについて
  • テクニックに溺れない
    • マルチスレッドの使用は避ける(ムズカシイため)。

よくつかう機能(準備中)

まとめようと思ったがムズイ・・・
サンプルをつくったほうが早いしわかりやすいような気がする。

  • ファイル操作
    • テキストファイル操作
  • 正規表現
  • 画像表示
  • 描画(準備中)

よくつかうコントロール(Windows Forms)(準備中)

コピペでとりあえずFormに載せれるサンプルをつくる予定

クラス名 ユーザインタフェース機能概要 補足 My テンプレ
TextBox 文字列を表示・入力する 設定(Multilineプロパティ)により、単一行 or 複数行に対応可能。
ComboBox 選択子から文字列を選んで表示・入力する ユーザーによる任意テキストの入力可 or 不可を設定可能。
NumericUpDown 数値を表示・入力する あらかじめ設定した範囲内の数値の入力を、直接 or 上下ボタンで入力できる。10進小数も扱える。Valueの型が扱いづらいかもしれない。
Button ボタンを表示する とりあえず何か試したいときは大体これにClickイベントを登録して使う。
MenuStrip メニューを表示する Buttonをダサく感じたり、込み入ってきたらコレ。 記事
PictureBox 画像を表示する 画像を表示させたり、キャンバスとして使える。
ListView リストを表示する Windowsのファイラであるエクスプローラのファイル一覧表示みたいなアレ。自分はいつもView = View.Detailsで使っており、行指向で表形式で表示するのに向いている。
TreeView 木構造を表示する 最初に手を出すにはハードル高め。
DataGridView 表形式で表示・入力する エクセルの表をイメージするとイメージ湧きやすいかも。クセ強めの印象。使いこなすには慣れが要りそう。

参考サイト


  1. ファイルを延々コピーしてしまってサーバーのディスクを一杯にしちゃったり、削除してはいけないファイルを削除しちゃったり、DoS攻撃しちゃったり、機密漏洩したり、、、人生オワタなトラブルを起しかねない。わりと気づかずに無限ループさせてしまったり入力間違いなどで簡単に起き得る。 

  2. 一部のウィルス対策ソフトは、使用実績の少ない実行ファイルを片っ端からウィルスとして検出するような、プログラマからするとクソ迷惑なタイプのものがあるので、割とそういう場面に出くわす。 

  3. 【コメント指摘を受けて内容見直しました】サクラエディタだと、括弧の閉じ忘れとかに気づきにくいので、Visual Studio Codeに比べると構文エラーを混入してしまいやすく、コンパイルエラーに慣れない初心者向けには厳しい。あとC言語向けにはデフォルトで色々入っているけどC#向けは手薄です。 

  4. GitHubがベストなようである。自分はとりあえずSubversionを使っている(職場でも使ってるので)。とっつきづらいようであれば、安定動作しているソースのバックアップをとっておくくらいはやっておいたほうがよい。(テキトウに上書き保存しつつやってたら元に戻れなくて泣く可能性大。) 

  5. mp3タグとかで規格上Shift_JIS使っちゃだめなところにShift_JIS使ってたりするものが氾濫してて、規格に反しているといえど実用上無視できない存在もあったりする。プログラムを作りこんだ後だと、簡単には適合できない場合があるので、プロトタイプを開発していろんなファイルを入力して試してしまうのが早い。もしくはオープンソースで対策しているソフトを漁ってみて参考にしたり(玄人向け)。 

  6. 学習目的であれば、フルスクラッチで作ってみるのもアリ。標準外のライブラリは、中身が把握できなかったり、環境やバージョン要因でうまく動作しなかったりするので、あえて使わないことも自分は多い。 

  7. 第三者がソースを見て一瞬でわかることをコメントに書く必要はないし、書くとかえって見づらくなる。また、ソースを改変したときにコメントが古いままで不整合な状態になるなど、悪影響を及ぼすことのほうが多い。学習用にメモとして書くのはいいけど、慣れてきたら不要なコメントは残さないほうがよい。 

  8. 機械学習については、表層を扱うだけなら数学的な知識がなくても使えるかもしれない。ただし、数学知識がないと機械学習の原理は理解できない。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Visual Studio要らずで始める C# ~ Windows向けツール開発 (かきかけ)

はじめに

プログラミングの上達への近道は、「とにかく作りたいものを決めてやってみる」に限る。

C#は、Windows7以降であれば標準でコンパイル環境が入っているので、テキストエディタさえあればすぐに始められます。
(「環境構築しようとしてパッケージ間のバージョンが合わない」というようなことで挫折する目に遭わずに済みます。)

追記:Excelマクロやバッチファイル以外の選択肢として、C#は強力なツールになると思います。(※適材適所ではあるので、使い分けが大事。)

追記

開発環境(Visual Studio)については、導入可能であれば、慣れのためにも入れたほうがよいです(外部ライブラリとかも取り込みやすいし、キーワード補完とかも便利)。

職場的に環境を入れれない場合は本記事の内容が参考になるかと思います。

とりあえずはじめよう

コマンドプロンプト(cmd.exe)を使います。この時点で不安がある人は、超入門 - 知識ゼロからコマンドプロンプトをつかう - Qiitaを見てね!

csc.exeでC#のソースコードをインプットとしてコンパイルします。
既に解説記事がたくさんあるので、調べてやってみてください。(核心部分を丸投げ。)
下記の記事が参考になるかと思います。

(※PowerShellである必要はなく、コマンドプロンプトで代用できます。)

上記でうまくいかない場合は、下記から探すかググるかして頑張ってください・・・。

初心者が心掛けるべきこと

  • 超重要 (身を守るために)
    • 職場などの本番環境で危ないこと1をやってはいけない。ファイル操作やネットワーク通信など。どんなことになるかは 本番環境でやらかしちゃった人 Advent Calendar 2019 - Qiita とかを見てみるのもよいかも。
    • 機密情報の管理に注意する。(とくに、ソースコードや入力用のテキストファイルにパスワードを書いたりする場合は、うっかり漏洩しないように注意。)
    • 職場のルールを守る。(ツールやライブラリの、使用やインストールなど。)
    • 自作ソフトがウィルスとして誤検知されることがあるので、その場合の対応についてあらかじめ考慮しておく2。(投げやり・・・)
    • 自作ソフトを使って業務や製品開発している場合に、自作ツールの不備が一要因となってトラブルや不具合混入や不具合見逃しなどを引き起すケースもあり得るので、無保証であることを全面的に周知しつつエビデンスを残すなりウィンドウのタイトルの先頭に【無保証】とつけるなり色々と保身を図ったほうがよい。
    • 他人の書いたソースコードやライブラリなどをローカル環境以外で使うケース(社内外に公開したり商用利用したりなど)では、著作権など国内外の法律に抵触しないかに注意する。
  • 開発環境(使用ツール)
    • 使いやすいテキストエディタを導入する。Undo&Redo,シンタックスハイライト,検索&置換は必須。Visual Studio Codeがベストだと思うが、サクラエディタとかでもキーワード設定とかしっかりやっておけば十分何とか使える3。メモ帳は無理。。
    • バージョン管理ツールをつかってソースコードのバージョン管理をする。4
    • テキスト差分比較ツールをいれておくとよい。
  • Windowsプログラミング(≒ツールづくり)で意識していること
    • 自動化しすぎない。汎用化しすぎない。(投入労力と有用性のトレードオフのポイントを定める。汎用的に作ろうとするとケアすべきことが爆発的に増えるので頓挫する。)
    • 独自色を出さない。(マニュアルが無くてもすんなり使えるのが理想)
    • 時間軸を意識する。(例. 途中で編集対象のファイル内容が変更されているかもしれない)
    • 変わりそうなところ(機能を追加したりするつもりのところ)を予測して、取り換えが利くように関数(メソッド)を分離するなどする。
    • 特定の種類のファイルを扱うとき - ファイルフォーマットの規格を調べる。
    • 特定の種類のファイルを扱うとき - 規格を守っていないデファクトスタンダードにも気を付ける5
  • あるものは使うべし
    • ありそうな機能は、標準ライブラリにあったり、誰かがソースを公開してたりするので、それを組み合わせて使うのが基本。6
  • 調べかた
    • わからないことはまずは調べる。
    • 堅苦しい本やリファレンス本を買うよりもネットで調べるべし。(「習うより慣れろ」なのと、この分野の本の情報(とくに特定の言語に特化したもの)はすぐ陳腐化する。)
    • ただしネットの情報は鵜呑みにしてはいけない。(バグってるコードとか、非推奨なコードが平気で転がっている。)
    • 掲示板とかブログなどの単発記事よりも、++c++;dobonなど、まとまっていて論理立った説明を多数書いてくれているサイトのものを採用するのがよい。(ただ、ニッチな内容を調べるときは掲示板とかを頼らざるを得ない場合もある。)
    • Qiitaの記事の信頼性については、各記事や著者次第なところがある。情報発信ツールであり、ノウハウ共有のためのツールであって、内容が常に正しいわけではない。自分自身もあまり裏どりせずに記事を書いてたり、「とりあえず動けばいいや」レベルのコードを投稿してたりする。
  • 文法知識
    • 変数の型とか、関数(メソッド)とか、制御構文(条件分岐、繰り返し)の書き方は、トライアンドエラーする前に、多少は理解してから着手したほうがよい。少なくとも代入文とかオーバーフローを知らずにコーディングし始めるのはNGだと思う。
    • とはいえ手を動かさないと始まらないので、まずはHello world的な入門コードをコピペって動作させるところから。
  • コーディング
    • インデントする。これ絶対! 参考: インデントをしよう!(プログラミング)
    • 極力コーディングスタイルを決めて統一する(市民権のあるスタイルからいずれかを採用しておいたほうがよい)。括弧とか空白,改行の位置、変数の大文字小文字など。
    • 判りやすい名前をつける。 hoge1 hoge2とかはヤメテ!
    • ソースにつけるコメントは必要十分に。7
    • 機能単位で関数(メソッド)単位に分けていく。
      イメージ:「〇〇を△△から検索する機能」「〇〇を入力として△△を表示する機能」
    • 慣れてきたら class を分解する。無理して使うのではなく、使うと楽になると思ったら使うのがよいかと。(感覚的には、1000行超えたあたりから、分解しないと管理できなくなってくる。)
  • コンパイル
  • 動作テスト
    • 細かい単位に分けて想定通りの動作をするかを試すのがよい。わりとビッグバンテスト的にやってしまいがちだが、不具合箇所を突き止めるのが大変。
  • 情報工学とか数学とか
    • プロとしてやっていくつもりなら、避けては通れない。
    • プログラムはなぜ動くのか とかの本を読んでみるのがよい(?)
    • メモリのアドレス空間とか、整数や浮動小数点数がデータがビット(0,1)の並びとしてどう表現されているかとか、原理的なことを知ると、色々なことがクリアに理解できるようになる。
    • 大規模なデータを扱うときは、アルゴリズムに関する知識が必要になってくる。(処理時間とかメモリリソースを意識する場面)
    • 音声処理や3Dグラフィックスや機械学習8などを取り扱う場合は、高校~大学レベルの数学の概念や計算規則をしっかり理解していないと厳しい。
  • 文字コードについて
  • テクニックに溺れない
    • マルチスレッドの使用は避ける(ムズカシイため)。

よくつかう機能(準備中)

まとめようと思ったがムズイ・・・
サンプルをつくったほうが早いしわかりやすいような気がする。

  • ファイル操作
    • テキストファイル操作
  • 正規表現
  • 画像表示
  • 描画(準備中)

よくつかうコントロール(Windows Forms)(準備中)

コピペでとりあえずFormに載せれるサンプルをつくる予定

クラス名 ユーザインタフェース機能概要 補足 My テンプレ
TextBox 文字列を表示・入力する 設定(Multilineプロパティ)により、単一行 or 複数行に対応可能。
ComboBox 選択子から文字列を選んで表示・入力する ユーザーによる任意テキストの入力可 or 不可を設定可能。
NumericUpDown 数値を表示・入力する あらかじめ設定した範囲内の数値の入力を、直接 or 上下ボタンで入力できる。10進小数も扱える。Valueの型が扱いづらいかもしれない。
Button ボタンを表示する とりあえず何か試したいときは大体これにClickイベントを登録して使う。
MenuStrip メニューを表示する Buttonをダサく感じたり、込み入ってきたらコレ。 記事
PictureBox 画像を表示する 画像を表示させたり、キャンバスとして使える。
ListView リストを表示する Windowsのファイラであるエクスプローラのファイル一覧表示みたいなアレ。自分はいつもView = View.Detailsで使っており、行指向で表形式で表示するのに向いている。
TreeView 木構造を表示する 最初に手を出すにはハードル高め。
DataGridView 表形式で表示・入力する エクセルの表をイメージするとイメージ湧きやすいかも。クセ強めの印象。使いこなすには慣れが要りそう。

参考サイト


  1. ファイルを延々コピーしてしまってサーバーのディスクを一杯にしちゃったり、削除してはいけないファイルを削除しちゃったり、DoS攻撃しちゃったり、機密漏洩したり、、、人生オワタなトラブルを起しかねない。わりと気づかずに無限ループさせてしまったり入力間違いなどで簡単に起き得る。 

  2. 一部のウィルス対策ソフトは、使用実績の少ない実行ファイルを片っ端からウィルスとして検出するような、プログラマからするとクソ迷惑なタイプのものがあるので、割とそういう場面に出くわす。 

  3. 【コメント指摘を受けて内容見直しました】サクラエディタだと、括弧の閉じ忘れとかに気づきにくいので、Visual Studio Codeに比べると構文エラーを混入してしまいやすく、コンパイルエラーに慣れない初心者向けには厳しい。あとC言語向けにはデフォルトで色々入っているけどC#向けは手薄です。 

  4. GitHubがベストなようである。自分はとりあえずSubversionを使っている(職場でも使ってるので)。とっつきづらいようであれば、安定動作しているソースのバックアップをとっておくくらいはやっておいたほうがよい。(テキトウに上書き保存しつつやってたら元に戻れなくて泣く可能性大。) 

  5. mp3タグとかで規格上Shift_JIS使っちゃだめなところにShift_JIS使ってたりするものが氾濫してて、規格に反しているといえど実用上無視できない存在もあったりする。プログラムを作りこんだ後だと、簡単には適合できない場合があるので、プロトタイプを開発していろんなファイルを入力して試してしまうのが早い。もしくはオープンソースで対策しているソフトを漁ってみて参考にしたり(玄人向け)。 

  6. 学習目的であれば、フルスクラッチで作ってみるのもアリ。標準外のライブラリは、中身が把握できなかったり、環境やバージョン要因でうまく動作しなかったりするので、あえて使わないことも自分は多い。 

  7. 第三者がソースを見て一瞬でわかることをコメントに書く必要はないし、書くとかえって見づらくなる。また、ソースを改変したときにコメントが古いままで不整合な状態になるなど、悪影響を及ぼすことのほうが多い。学習用にメモとして書くのはいいけど、慣れてきたら不要なコメントは残さないほうがよい。 

  8. 機械学習については、表層を扱うだけなら数学的な知識がなくても使えるかもしれない。ただし、数学知識がないと機械学習の原理は理解できない。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# - メニューをつくろう。MenuStripのサンプル コードべた書き(Visual Studio不使用)

MenuStripを使うサンプルソースはサクッと見つかったけど、重なり防止の部分のケアが欲しかったので、メモ代わりに置いておきます。

画面キャプチャ

image.png

image.png

ソースコード

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

class MenuStripSample:Form
{
    MenuStripSample()
    {
        ClientSize = new Size(500, 300);


        var menuStrip = new MenuStrip();

        SuspendLayout();
        menuStrip.SuspendLayout();


        var menuItemFile       = new ToolStripMenuItem(){ Text = "ファイル(&F)"};
        var menuItemFileExport = new ToolStripMenuItem(){ Text = "エクスポート"};
        var menuItemEdit       = new ToolStripMenuItem(){ Text = "編集(&E)"};
        menuStrip.Items.Add(menuItemFile);
        menuStrip.Items.Add(menuItemEdit);

        menuItemFile.DropDownItems.Add( new ToolStripMenuItem("開く(&O)...", null, (s,e)=>{MessageBox.Show("ひらく!");}, Keys.Control | Keys.O) );
        menuItemFile.DropDownItems.Add( new ToolStripSeparator() );
        menuItemFile.DropDownItems.Add( new ToolStripMenuItem("保存(&S)", null, (s,e)=>{MessageBox.Show("ほぞん!");}, Keys.Control | Keys.S) );
        menuItemFile.DropDownItems.Add( new ToolStripSeparator() );
        menuItemFile.DropDownItems.Add( menuItemFileExport );

        menuItemFileExport.DropDownItems.Add(new ToolStripMenuItem("bmpファイルとしてエクスポート", null, (s,e)=>{MessageBox.Show("えくすぽーと その1");}, null) );
        menuItemFileExport.DropDownItems.Add(new ToolStripMenuItem("pngファイルとしてエクスポート", null, (s,e)=>{MessageBox.Show("えくすぽーと その2");}, null) );

        menuItemEdit.DropDownItems.Add( new ToolStripMenuItem("ほげほげ", null, (s,e)=>{MessageBox.Show("ほげ");}, null) );
        menuItemEdit.DropDownItems.Add( new ToolStripMenuItem("Foo Bar",  null, (s,e)=>{MessageBox.Show("Foo");}, null) );


        ///////
        // メニュー以外を作成しているコード部分
        var panel = new Panel(){Dock = DockStyle.Fill};
        Controls.Add(panel);

        var btn1 = new Button(){Location = new Point(0,0), Size = new Size(100,30), Text = "ボタン1"};
        var btn2 = new Button(){Dock = DockStyle.Bottom,   Height = 30,             Text = "ボタン2"};
        btn1.Click += (s,e)=>{MessageBox.Show("ボタン1が押されました");};
        btn2.Click += (s,e)=>{MessageBox.Show("ボタン2が押されました");};
        panel.Controls.Add(btn1);
        panel.Controls.Add(btn2);
        //
        ///////

        Controls.Add(menuStrip);   // 注意:panel より先に登録してしまうと、panelとmenuStripが重なって表示されてしまう。
        MainMenuStrip = menuStrip;

        menuStrip.ResumeLayout(false);
        menuStrip.PerformLayout();
        ResumeLayout(false);
        PerformLayout();
    }

    [STAThread]
    static void Main()
    {
        Application.Run(new MenuStripSample());
    }
}

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Office アドイン作成方法【C#】初心者向け

Microsoftから出されているデスクトップ版 Officeアドイン(office2013以降に対応)の作成方法について書かせて頂きます。

正直、C#のコードが少しわかるというレベルの人、というか今回の記事なら知らなくても、簡単に作成できます。
(リボンのボタンを作るところまではC#のコードを自分では一切書いてないです(笑))

開発環境

・アドインを開発したい Officeアプリ(Excel,Word,PowerPoint,Outlookなど)
・Visual Studio Community

環境構築

まず、Visual Studioのインストールです。
(Officeのインストール手順は、わざわざここで書くことではないと思いますので飛ばします)
Microsoftの公式サイトからダウンロードします。
下記がそのリンクです。
https://visualstudio.microsoft.com/ja/downloads/
スクリーンショット 2020-10-03 115516.png
ダウンロードが終わったら、インストーラーを起動してインストールを実行します。
確認画面が立ち上がるかと思いますが、確認事項に問題がなければ続行していってください。
ある程度進むと下記の様な画面になるかと思います。
この画面の下の方にいくと「Office/SharePoint開発」があるので、チェックを入れた後、画面右下の「インストール」を押下して実行してください。
(筆者の環境では既にインストール済みなのでボタンが「変更」になっています。このタイミングで「Office/SharePoint開発」の追加を忘れた場合でも、再度インストーラーを起動すれば追加することができます)
スクリーンショット 2020-10-03 115516.png
インストールが終わったら、プロジェクトを作成します。
Visual Studioを起動すると下記の様な画面になるため、まずは「新しいプロジェクトの作成」を押下します。
スクリーンショット 2020-10-03 115516.png
すると、作成したいプロジェクトの種類を選択する画面に遷移します。
ここで作成するアドインの種類に合わせてプロジェクトを選択するのですが、今回は例としてExcel VSTOアドインを選びます。(「C#」「Windows」「Office」で絞り込むと見つけやすいです。)
image.png
「次へ」を押下すると下記の様な画面になるかと思います。
ここでは今回作成するプロジェクト名を設定します。こうしなければいけないとかいう決まりはないのでお好みでどうぞ(笑)
(こういうと、キャメルケースだとかスネークケースだとか言い出す人がいるかもしれませんが…)
image.png
上記画面で「作成」ボタンを押下すると、ついにプロジェクトが出来上がります。
さあ、これからコードを書くぞ!というところなのですが、まあ、今回はコード書くのは後回しにします。
最小限のコーディングで初心者でもわかる範囲に留めておこうと思います。
(私も細かいことを書くのが面倒くさいので)
まずは、Excelにリボンを追加します。画面右側に「ソリューションエクスプローラー」があるので、プロジェクト名を右クリックします。下記の左側の画像でいうと「ExcelAddin1」のところです。
右クリックで右側の画像と同じメニューが表示されると思いますので「追加」の「新しい項目」を押下してください。
一つ下の画像と同じウィンドウが立ち上がると思います。
image.png
image.png
今回はリボンを作成するので、「リボン(ビジュアルなデザイナー)」を選択してください。ファイル名はお好みで(笑)
ファイル名が決まったなら「追加」を押下してください。下記画像の様なデザイナーが表示されると思います。
image.png
とりあえず、ボタンを追加します。下記画像のように「表示」の中の「ツールボックス」を選択してください。
右の画像の様なウィンドウが出てくると思います。その中から「Button」をリボンのデザイナーの中にドラッグ&ドロップしてください。(注意:リボンのデザイナーウィンドウ内を押下してからでないとツールボックス内にアイテムが表示されない場合があります)
image.png
これによりついに!ボタンが出来上がります!(大した事やってないですが(笑))
ただ、このままだとボタンを押しても何も反応しません。せっかくなので機能を追加したいと思います。
デザイナー上のボタンを Wクリックしてください。
image.png
下記の様なテキストエディタ画面に移ると思います。その中の「button1(自身で付けたボタン名)_Click」の項目の{ }で囲まれた範囲に「System.Windows.Forms.MessageBox.Show("ボタンが押下されました");」と記入し、Saveして下さい。
保存したら画面上部の「開始」を押下してください。(本来なら先に「ビルド」するのですが、そこまで本格的に書くと面倒くさい細かくなるので飛ばします)
image.png
勝手にExcelが立ち上がったかと思います。(Excelが入ってなければもちろん立ち上がりません(笑))
シートを開くとリボンの中に「アドイン」という項目が追加されているかと思います。
そして、「アドイン」の項目中には先ほど追加した「button1」が!ボタンを押下すると!
メッセージボックスが表示される!ということです。
image.png
そういえば、リボンのデザインについて書いてなかったので書いておきます。
先ほどのデザイナー画面に戻ってもらって、ボタンをクリックした後、画面右下の「プロパティ」ウィンドウの中の「Label」項目を探し出し、テキストをお好みに書き換えてください。中身が確定するとボタンのラベルが書き換わるかと思います。
(注意:Excelを閉じてから変更しないと、Visual Studioがブツブツ言ってきます(笑))
あと、「OfficeImageId」という項目があるのですが、そこに特定の文字列を設定するとラベルのアイコンを設定することができます。
「officeimageid アイコン」みたいな検索内容で検索すると見つかると思うので、アイコンを設定したいという方はぜひ調べてみてください。
image.png

まとめ(雑談)

結構長々と書かせていただきましたね。わかりやすいようにと思って画像を結構な枚数作って貼ったのですが、それが正直面倒でした(笑)
初心者の方に伝わるようにと思って書いたので、プログラミング初心者でもわかるかと思います。そもそもコードに関しては一行しか書いていないですし(笑)
後日、アドインからExcelのシート内のセルをいじる方法と、Excelのイベントを設定する方法を書こうかと思います。
待ってられないという方は、ネットで探してください(笑)
似たようなことが書かれた記事がたくさんありますので、探せばすぐに見つかるかと思います(ネットは偉大)

ここからは雑談です。(ただ自分が書きたいだけです(笑))
この間、仕事で AutoCAD のアドインを C#で書いたんですが、すっごい面倒くさかったです(笑)
Officeのアドインみたいにデザイナーでリボンを作るための APIがあるみたいなんですが(本当にあるのかは不明)、会社のPCだと会社側に管理されているせいで、そのAPIが取り込めない!
ということで、わざわざリボンを C#のコードで一から書いたんですが、とにかく面倒くさかった(笑)
サンプルのコードが少なすぎる!そして書き方がわかりずらい!
と思いながらなんとか仕上げました(笑)
今では、今回のExcelのアドインみたいに起動したら勝手にリボン作成まで処理してくれます。自動で読み込む部分を書くのもややこしかったですが(笑)
機会があれば AutoCADアドインについて書こうと思います。(リクエストがあれば優先的に書きます(笑))

そういえば、Wordのアドインでドラッグ&ドロップの処理がうまくできないとかいうことがありました。代替案として、始めはマウスハックとかいう方法で処理を行っていたのですが、Windows.Form.Application.Idleというイベントと、C#純正でマウスの状態を取得する方法があるので、それを使えば上手くいきました。コードも少なくて済むので個人的にはこちらの方が良いんじゃないかと思っています。後日、もう少し詳しい記事を書こうと思います。

あと、最近仕事で C#のコードばかり書いていて多少ネタがあるので機会があれば書こうと思います。「一定時間で消えるメッセージボックスをC#純正のコードだけで」とか、「C#からトースト通知を出す方法」とかとか…。

最後に、最近仕事でRustのコードを書いているので、Rustの記事を近いうちに書こうと思います。(最後だけ C#じゃない(笑))
Rustのコードは正直、自分にはわかりにくいです(笑)
書きなればそんなことはないんでしょうが、というかそれはどの言語でも同じですね…。
最初の方は他の.rsファイルを読み込むためのフォルダ構成を作るだけでもややこしく感じましたし、最近はWebAssemblyを作るということになったんですが、そもそも自分は WebAssemblyを知らないし、JavaScriptですら余り詳しくないという不適合すぎる条件でなんとか作成しました。(すごい初歩的なところでつまずきました(笑))
その経験を記録しておこうと思います(笑)

雑談が長い!ということで今回はここで終わります。最後までお付き合い頂きありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む