[C#]PIC16シリーズのバイナリを逆アセンブルする

前回、MPLABで生成されたhexファイルを解析するプログラムをC#で用意しました。


今回はこのバイナリ(機械語)を元に、逆アセンブルするプログラムを作成します。

というわけでプログラムです。
13bitの2進データをみて文字列に変換するだけなので、単純に力技です。
今回は分かりやすいように全命令の処理を列挙しましたが、PICの機械語命令は、命令の各bitに何の情報を持たせるかの構成パターン数が少ないので、最適化させればもう少しシンプルに書けそうです。
PICアセンブラ入門



using System;
 
class Pic16f84 {
    //***************************************************************************
    /// <summary>   指定された機械語の命令をアセンブリのニーモニックに変換する
    ///             PIC16F84は命令長が13bitなので、13bit分のみチェックする
    /// </summary>
    /// <param name="inMachineCode">    機械語命令</param>
    /// <param name="isLittleEndian">   リトルエンディアンか(通常はfalse)</param>
    /// <returns>   ニーモニック文字列</returns>
    //***************************************************************************
    public static string toNimonic( string inMachineCode, bool isLittleEndian = false ) {
        int mCode;
        if ( isLittleEndian ) {
            mCode = Convert.ToInt16( inMachineCode.Substring(2,2) + inMachineCode.Substring(0,2), 16 );
        } else {
            mCode = Convert.ToInt16( inMachineCode, 16 );
        }
 
        if ( mCode == 0x3FFF ) {
            return "";
        }
 
        // RETURN:  0000 0000 0000 1000
        if ( mCode == 0x0008 ) {
            return "RETURN";
        }
 
        // RETFIE:  0000 0000 0000 1001
        if ( mCode == 0x0009 ) {
            return "RETFIE";
        }
 
        // SLEEP :  0000 0000 0110 0011
        if ( mCode == 0x0063 ) {
            return "SLEEP";
        }
 
        // CLRWDT:  0000 0000 0110 0100
        if ( mCode == 0x0064 ) {
            return "CLRWDT";
        }
 
        // TODO:仕様書を要確認
        // NOP   :  0000 0000 0xx0 0000
        if ( mCode == 0x0000 ) {
            return "NOP";
        }
 
        // MOVWF :  0000 0000 1fff ffff
        if ( ( mCode & 0xFF80 ) == 0x0080 ) {
            return "MOVWF " + getOperandRegister( mCode );
        }
 
 
        // CLRW  :  0000 0001 0xxx xxxx
        if ( ( mCode & 0xFF80 ) == 0x0100 ) {
            return "CLRW";
        }
 
        // CLRF  :  0000 0001 1fff ffff
        if ( ( mCode & 0xFF80 ) == 0x0180 ) {
            return "CLRF " + getOperandRegister( mCode );
        }
 
        // SUBWF :  0000 0010 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0200 ) {
            return "SUBWF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // DECF  :  0000 0011 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0300 ) {
            return "DECF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // IORWF :  0000 0100 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0400 ) {
            return "IORWF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // ANDWF :  0000 0101 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0500 ) {
            return "ANDWF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // XORWF :  0000 0110 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0600 ) {
            return "XORWF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // ADDWF :  0000 0111 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0700 ) {
            return "ADDWF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // MOVF  :  0000 1000 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0800 ) {
            return "MOVF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // COMF  :  0000 1001 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0900 ) {
            return "COMF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // INCF  :  0000 1010 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0A00 ) {
            return "INCF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // DECFSZ:  0000 1011 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0B00 ) {
            return "DECFSZ " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // RRF   :  0000 1100 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0C00 ) {
            return "RRF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // RLF   :  0000 1101 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0D00 ) {
            return "RLF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // SWAPF :  0000 1110 dfff ffff
        if ( ( mCode & 0xFF00 ) == 0x0E00 ) {
            return "SWAPF " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // INCFSZ:  0000 1111 dfff ffff
            if ( ( mCode & 0xFF00 ) == 0x0F00 ) {
            return "INCFSZ " + getOperandRegister( mCode ) + ", " + getDestination( mCode );
        }
 
        // BCF   :  0001 00bb bfff ffff
        if ( ( mCode & 0xFC00 ) == 0x1000 ) {
            return "BCF " + getOperandRegister( mCode ) + ", " + getOperandBit( mCode );
        }
 
        // BSF   :  0001 01bb bfff ffff
        if ( ( mCode & 0xFC00 ) == 0x1400 ) {
            return "BSF " + getOperandRegister( mCode ) + ", " + getOperandBit( mCode );
        }
 
        // BTFSC :  0001 10bb bfff ffff
        if ( ( mCode & 0xFC00 ) == 0x1800 ) {
            return "BTFSC " + getOperandRegister( mCode ) + ", " + getOperandBit( mCode );
        }
 
        // BTFSS :  0001 11bb bfff ffff
        if ( ( mCode & 0xFC00 ) == 0x1C00 ) {
            return "BTFSS " + getOperandRegister( mCode ) + ", " + getOperandBit( mCode );
        }
 
        // CALL  :  0010 0kkk kkkk kkkk
        if ( ( mCode & 0xF800 ) == 0x2000 ) {
            return "CALL " + getLiteral11( mCode );
        }
 
        // GOTO  :  0010 1kkk kkkk kkkk
        if ( ( mCode & 0xF800 ) == 0x2800 ) {
            return "GOTO " + getLiteral11( mCode );
        }
 
        // MOVLW :  0011 00xx kkkk kkkk
        if ( ( mCode & 0xFC00 ) == 0x3000 ) {
            return "MOVLW " + getLiteral8( mCode );
        }
 
        // RETLW :  0011 01xx kkkk kkkk
        if ( ( mCode & 0xFC00 ) == 0x3400 ) {
            return "RETLW " + getLiteral8( mCode );
        }
 
        // IORLW :  0011 1000 kkkk kkkk
        if ( ( mCode & 0xFF00 ) == 0x3800 ) {
            return "IORLW " + getLiteral8( mCode );
        }
 
        // ANDLW :  0011 1001 kkkk kkkk
        if ( ( mCode & 0xFF00 ) == 0x3900 ) {
            return "ANDLW " + getLiteral8( mCode );
        }
 
        // XORLW :  0011 1010 kkkk kkkk
        if ( ( mCode & 0xFF00 ) == 0x3A00 ) {
            return "XORLW " + getLiteral8( mCode );
        }
 
        // SUBLW :  0011 110x kkkk kkkk
        if ( ( mCode & 0xFE00 ) == 0x3C00 ) {
            return "SUBLW " + getLiteral8( mCode );
        }
 
        // ADDLW :  0011 111x kkkk kkkk
        if ( ( mCode & 0xFE00 ) == 0x3E00 ) {
            return "ADDLW " + getLiteral8( mCode );
        }
 
        // 不明
        return "???";
    }
 
 
    //***************************************************************************
    /// <summary> BCF,BSF, BTFSC命令などで指定されるbit位置情報を返す
    ///           ビット位置情報はOPCODE<9-7>で指定される。
    /// </summary>
    /// <returns></returns>
    //***************************************************************************
    static private string getOperandBit( int mCode ) {
        int bitOffset = ( mCode & 0x0380 ) >> 7;
        return "0x" + bitOffset.ToString( "x" );
    }
 
    //***************************************************************************
    /// <summary> 命令実行結果の格納先(W or F)を返す
    /// </summary>
    /// <returns></returns>
    //***************************************************************************
    static private string getDestination( int mCode ) {
        int data = ( mCode & 0x0080 ) >> 7;
        if ( data == 0 ) {
            return "W";
        } else {
            return "F";
        }       
    }
 
    //***************************************************************************
    /// <summary> ADDWF, MOVF命令などで指定される処理対象レジスタの情報を返す
    ///           レジスタの場所情報はOPCODE<6-0>で指定される。
    /// </summary>
    /// <returns></returns>
    //***************************************************************************
    static private string getOperandRegister( int mCode ) {
        int regAddress = ( mCode & 0x007F );
        return "0x" + regAddress.ToString( "x" );
    }
 
 
    //***************************************************************************
    /// <summary> 11桁のリテラル値を返す
    ///           リテラル値の場所はOPCODE<10-0>
    /// </summary>
    /// <param name="mCode"></param>
    /// <returns></returns>
    //***************************************************************************
    static private string getLiteral11( int mCode ) {
        int regAddress = ( mCode & 0x07FF );
        return "0x" + regAddress.ToString( "x" );
    }
 
    //***************************************************************************
    /// <summary> 8桁のリテラル値を返す
    ///           リテラル値の場所はOPCODE<7-0>
    /// </summary>
    /// <param name="mCode"></param>
    /// <returns></returns>
    //***************************************************************************
    static private string getLiteral8( int mCode ) {
        int regAddress = ( mCode & 0x00FF );
        return "0x" + regAddress.ToString( "x" );
    }
}




で、こちらが上記クラスの利用者側プログラムのサンプルです。
MPLABでアセンブル可能なコードを出力させています。

//------------------------------------
// hexファイルを元に,逆アセンブルを行う
//------------------------------------
textBox1.Text = "";
textBox1.AppendText( "LIST    P=16F84"     + Environment.NewLine );
textBox1.AppendText( "INCLUDE P16F84A.INC" + Environment.NewLine );
textBox1.AppendText( "ORG " + startAddr + Environment.NewLine );
bool needOrgFlg = false;
 
int dispLine = 1;
for ( int curAddr = startAddr; curAddr < endAddr; curAddr+=2 ) {
    string mWord1 = parser.getValue( curAddr );
    string mWord2 = parser.getValue( curAddr+1 );
    string mWord  = mWord1 + mWord2;
    string mnemonic = Pic16f84.toNimonic( mWord, true );
 
    //------------------------------
    // .asmファイル用のコードを出力
    //------------------------------
    if ( mnemonic.Length <= 0 ) {
        needOrgFlg = true;
    } else {
        if ( needOrgFlg ) {
            textBox1.AppendText( Environment.NewLine );
            textBox1.AppendText( "ORG " + ( curAddr /2 ) + Environment.NewLine );
        }
        needOrgFlg = false;
        textBox1.AppendText( mnemonic + Environment.NewLine );
    }
}



全命令が網羅されたプログラムで試したわけでは有りませんが、アセンブリで100ステップ程のプログラムをアセンブル->逆アセンブル->再度アセンブルしてみたところ、同一hexファイルが生成されたので、正しく逆アセンブル出来てるっぽいです。






また、MPLABの逆アセンブル結果出力互換のコードを生成するプログラムも用意したので公開します。
一部オペランドの指定の基数が10進と16進で異なる部分がありますが、それ以外はdiffをとっても同じ出力になりました。

//------------------------------------
// hexファイルを元に,逆アセンブルを行う
//------------------------------------
txtDisAsmWithAddr.Text = "";
txtDisAsmWithAddr.AppendText( " Line  Address  Opcode               Disassembly              " + Environment.NewLine );
 
 
int dispLine = 1;
for ( int curAddr = startAddr; curAddr < endAddr; curAddr+=2 ) {
    string mWord1 = parser.getValue( curAddr );
    string mWord2 = parser.getValue( curAddr+1 );
    string mWord  = mWord1 + mWord2;
    string mnemonic = Pic16f84.toNimonic( mWord, true );
 
    //---------------------------
    // MPLAB互換のダンプを出力
    //---------------------------
    txtDisAsmWithAddr.AppendText( String.Format( "{0,6}", dispLine.ToString() ) +
                          "   " +
                          ( curAddr / 2  ).ToString( "X3" ) +           /* addrはbyteでは無くword(16bit)なので2で割る */
                          "     " +
                          mWord2 + mWord1 +
                          "  " +
                          mnemonic + Environment.NewLine );
    dispLine++;
}



MPLAB側の逆アセンブル出力は、以下の操作でファイルに落とすことが出来ます。

MPLABのメニューバーに有るViewよりProgram Memoryを選択します。


表示されたウィンドウの適当なところを右クリックして、Output To Fileをクリックします。


ファイル保存ダイアログが表示されます。



4501532300
PICアセンブラ入門

関連記事

コメントを残す

メールアドレスが公開されることはありません。