前回、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をクリックします。
ファイル保存ダイアログが表示されます。
PICアセンブラ入門
関連記事
コメントを残す