スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

バイナリをつつく19-愛のメモリー5依存する愛はいらない

インドリ「ドリィちゃん、ボクが何をしたって言うんだい?」
インドリィちゃん「惚けるつもり?貴方がそのつもりなら・・・」
ドリィちゃんはおもむろにインドリからキーボードを取り上げるとアクセス修飾子を変更し始めた。

注目するべき部分だけ抜粋

protected uint EAX {
    get { return this.eax.ValueU32; }
    set { this.eax.ValueU32 = value; }
}

protected ushort AX {
    get { return this.eax.ValueU16; }
    set { this.eax.ValueU16 = value; }
}

protected byte AH {
    get { return this.eax.ValueHighByte; }
    set { this.eax.ValueHighByte = value; }
}

protected byte AL {
    get { return this.eax.ValueLowByte; }
    set { this.eax.ValueLowByte = value; }
}

インドリ「なっ何を、これじゃレジスタがマシンオブジェクトから見えないじゃないか。」
さらにドリィちゃんは、インドリが実装したイベント関係のコードを全て消した。
インドリィちゃん「見えなくしているのよ。見えるから依存するのよ。」
インドリ「ドリィちゃん何のことを言っているの?」
インドリィちゃん「CPUオブジェクトのレジスタプロパティに直接触れるということは、 マシンオブジェクトがCPUオブジェクトのプロパティに依存しちゃう。それじゃあ駄目。」
インドリ「何がいけないのさ。互いに協力し合えばいいじゃないか?」
インドリィちゃん「CPUはIntel製だけじゃないの。他にもARM、PowerPCとか色々あるのよ。 それにオブジェクトが依存しすぎると再利用しにくいのよ。依存しすぎるとろくでも無しに人生壊されるわ。」
インドリ「・・・今さらりと凄いこと言ったような・・・おっほん。でもさぁ、現実にマザーボードによってのせられるCPUが限定されているからいいじゃない?」
インドリィちゃん「マザーボードオブジェクト作る気ならば・・・」
インドリ「ちょっ、今のはなし。わかったよ。」
インドリィちゃん「始めからそういえばいいのよ。それに直接レジスタが操作できるんだったらMOV命令の意味がないじゃない。」
インドリ「うっそれもそうだ。でもじゃあどうやってレジスタに値を設定するの。」
インドリィちゃん「まったく、もぉ。だからMOV命令とイベントがあるんでしょ。」
インドリ「あっそうか!新しいイベントを作ればいいんだね。」
インドリィちゃん「ちょっと待ちなさい。既にマシンオブジェクトにイベント定義しているわよね。 何故それを使わなかったの?」
インドリ「ドリィちゃんが作れって言ったけど面倒だからね。」
インドリィちゃん「やっぱり裏切ったのねぇぇぇぇ。」
インドリ「ごめんごめん。ちゃんと作るから許して。」
インドリは身の危険を感じあわててマシンオブジェクトのイベントを変えた。
その定義をみんなも見てみよう。

//データをメモリへ送信するイベントのデータ
public abstract class SendDataEventArgs
{
    private byte[ ] datas;
    public byte[ ] SendDatas {
        get { return datas; }
    }

    public SendDataEventArgs( byte[ ] datas ) {
        this.datas = datas;
    }

}

//IntelCPU用データ送信イベントの情報
public class SendDataToIntelEventArgs : SendDataEventArgs
{
    private RegisterName target;
    public RegisterName Target {
        get { return target; }
    }

    public SendDataToInterlEventArgs( byte[ ] datas, RegisterName target )
        : base( datas ) 
    {
        this.target = target;
    }
}

インドリ「インドリちゃんこれでいい?」
インドリィちゃん「貴方にしてはいいほうね。っと言いたいところだけど前のイベント定義間違っていたわよ。」
インドリ「ピィ~」

//メモリデータが必要なことを通知するイベントの情報
public class RequestMemoryDataEventArgs : EventArgs
{
    private uint start;
    public uint StartAddress {
        get { return start; }
        set { start = value; }
    }

    private byte length;
    public byte DataLength {
        get { return length; }
        set { length = value; }
    }

    public RequestMemoryDataEventArgs( uint startAddress, byte dataLength ) {
        this.start = startAddress;
        this.length = dataLength;
    }
}

public class RequestMemoryDataToIntelEventArgs : RequestMemoryDataEventArgs
{
    private RegisterName register;

    public RegisterName Target {
        get { return register; }
        set { register = value; }
    }

    public RequestMemoryDataToIntelEventArgs( uint startAddress, 
        byte dataLength, 
        RegisterName register ) : base(startAddress, dataLength)
    {
        this.register = register;
    }
}

インドリ「あれ?でもドリィちゃん。 このイベントを定義しても結局Machineオブジェクトに実装してしまうとCPUオブジェクトに依存してしまうピッヨ。」
インドリィちゃん「それはねぇ、・・・・・秘密よ。」
インドリ「ちょっ。」
インドリィちゃん「冗談はさておき次回へ続く。」
インドリ「えぇ~そりゃないよ~」
説明が長くなるので次回へ続く。
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

バイナリをつつく18-愛のメモリー4私の愛を受け取れますか?

インドリ「何、この思わせぶりなタイトルは?まぁいいか。じゃあ早速逆にレジスタにメモリデータを設定する場合を考えてみよう。」
インドリはまたしてもIntelマニュアル中のA-6・オペコードマップを見た。
インドリ「そんなこと言われても見るしかないピヨね。さくっと発見したよ。オペコードマップは同じ命令が同じ行にかたまっている場合が多いんだ。 じゃ~ん。行A・列1にあったピヨッ。 早速実装ダぁ~ダァ~だァ~。エコー付けてみたよ。」

バサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ

注目するべき部分だけ抜粋
public static OpeCodeInfo SearchOpeCode( byte value ) {
    case 10:
        switch ( col ) {
            case 3:
                info.Name = CommandName.MOV;
                info.NextType = 
           NextInfoType.SourceVaryRegisterDestinationAddressing;
                info.DestinationRegister = RegisterName.eAX;
                info.SourceAddressMode = AddressingMode.O;
                info.SourceOperand = OperandType.v;
                break;

         }
}
インドリ「この部分は毎度お馴染みだね。。次は命令解析部分だぁ~、鳥ゃぁ~」

バサバサバサバサバサバサバサバ鳥ゃー鯖やーサバサバサバサバサバサバサバサバサバサバサバサバサバサ

注目するべき部分だけ抜粋
public void AnalyzeCommand( ) {
    foreach ( byte data in datas ) {
        OpeCodeInfo info = SearchOpeCode( data );
        Command cmd = new Command( );
        switch ( info.NextType ) {
            case NextInfoType.SourceVaryRegisterDestinationAddressing:
                //しまった!まだ実装していない。 
                cmd.DestinationRegister = GetRegister( info );
                SearchModRM( ref cmd );
                break;
    }
    cmds.Enqueue(cmd);
}
インドリ「んまり変わらないね。次はついに命令の実行だー。犯人はお前ダー。」

夏ゃーバサバサ海だ~バサバサプログラミングだ~バサバサバサバサバサバサバサバサバサバサバサバサバサバサ

注目するべき部分だけ抜粋
public void ExecuteCommand( ) {    
    foreach ( Command cmd in cmds ) {
        //途中省略
        switch ( cmd.Name ) {
            case CommandName.MOV:
                Mov( cmd );
                break;
        }
    }

}

注目するべき部分だけ抜粋
private void Mov( Command cmd ) {
    switch ( cmd.NextType ) {
        case NextInfoType.SourceVaryRegisterDestinationAddressing:
            //CPUを持つPCのメモリにデータを送信。
            ReceivesData( 
                cmd.SourceAddress, 
                cmd.DestinationValueLength, 
                cmd.DestinationValue );
            break;
    }
}

インドリ「次はCPUを保持するマシンオブジェクトの機能を実装するだけだ。」

これで終わりダーバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ

public Machine( ) 
{
    this.cpu = new ControlProcessingUnit( this );
    this.memory = new byte[ 1024 ]; //ひとまずメモリを1K用意する
    //データがCPUから送られてきた事が一目でわかる名前にする
    this.cpu.SendDataEvent += 
        new ControlProcessingUnit.MemoryAccessEventHandler
        ( ReceivesMemoryData );

    //CPUがデータを要求していることが一目でわかる名前にする
    this.cpu.ReceivesDataEvent += 
        new ControlProcessingUnit.MemoryAccessEventHandler
        ( NotifyMemoryDataEvent );
}


後で解説
private void ReceivesMemoryData( object sender, MemoryAccessEventArgs e ) {
   SetMemoryData( e.StartAddress, e.Value, e.DataLength );
}

注目するべき部分だけ抜粋
private void NotifyMemoryDataEvent( object sender, 
    ReceivesMemoryDataEventArgs e ) 
{
    switch ( e.Target ) {
        case RegisterName.EAX:
            this.cpu.EAX = e.Value;
            break;
    }
}
インドリ「ピやったぁ。完成だピヨ。残りはSetMemoryとかを解説するだけだね。」
インドリィちゃん「私をだましたわねぇぇぇぇ。」
インドリ「えっそれどういう意味?」
(それより何よりもまずい!!!いつもよりも語尾が長い。こりゃかなり怒っているぞ。)
騙したとは一体何のことだろうか?次回へ続く。

テーマ : プログラミング
ジャンル : コンピュータ

バイナリをつつく17-愛のメモリー3

インドリ「あれっ?ここは何処?何をしていたんだっけ・・・」
インドリはいきなり意識を断たれたので暫くの間今おかれている間状況が把握できなかった。
インドリ「うーんとえーと・・・はっそうだ!MOV命令の実装をしていたんだ。それで・・・」
インドリはドリィちゃんが傍に居ないか恐々回りを伺った・・・その結果、幸い居ないことが確認できた。
インドリ「よし鬼の居ぬ間に洗濯だ!」
インドリは何時もの様にIntelマニュアル中のA-6・オペコードマップを見た。 そして、行A・列3で目的のMOV命令を発見した。
インドリ「早速実装だ!まずはオペコードマップの実装だぁ~」

鳥ゃーバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ

注目するべき部分だけ抜粋
public static OpeCodeInfo SearchOpeCode( byte value ) {
    case 10:
        switch ( col ) {
            case 3:
                info.Name = CommandName.MOV;
                info.NextType = NextInfoType.DestinationAddressAndRegiser;
                info.DestinationAddressMode = AddressingMode.O;
                info.DestinationOperand = OperandType.v;
                info.SourceRegister = RegisterName.eAX;
                break;

         }
}
インドリ「次は命令解析部分だぁ~、おりゃぁ~」

鳥ゃーバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ

注目するべき部分だけ抜粋
public void AnalyzeCommand( ) {
    foreach ( byte data in datas ) {
        OpeCodeInfo info = SearchOpeCode( data );
        Command cmd = new Command( );
        switch ( info.NextType ) {
            case NextInfoType.DestinationAddressAndRegiser:
                //しまった!まだ実装していない。 
                 cmd.SourceRegister = GetRegister( info );
                 SearchModRM( ref cmd ); 
                 break;
    }
    cmds.Enqueue(cmd);
}
インドリ「GetRegisterとSearchModRMについては改めて解説するよ。次はついに命令の実行だー。犯人はお前ダー。」
br> 鳥ゃーバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ

注目するべき部分だけ抜粋
public void ExecuteCommand( ) {    
    foreach ( Command cmd in cmds ) {
        //途中省略
        switch ( cmd.Name ) {
            case CommandName.MOV:
                Mov( cmd );
                break;
        }
    }

}

注目するべき部分だけ抜粋
private void Mov( Command cmd ) {
    switch ( cmd.NextType ) {
        case NextInfoType.DestinationAddressAndRegiser:
            //CPUを持つPCのメモリにデータを送信。
            SendData( 
                cmd.DestinationAddress, 
                cmd.SourceValueLength, 
                cmd.SourceValue );
            break;
    }
}

インドリ「よし!今日は調子がいいぞ。きりが良いので今回の記事は終えて、 次の記事で、逆にレジスタにメモリ内のデータを受け取る場合を書くよ。今から書くからちょっとだけ待ってね。」

テーマ : プログラミング
ジャンル : コンピュータ

バイナリをつつく16-愛のメモリー2

?「インドリおきて・・・」
インドリ「(ああ誰かの可愛い声がする)」
?「インドリおきて・・・」
インドリ(あれ、ドリィちゃん?僕を看病してくれたのかな?もしかして僕のこと・・・)
インドリィちゃん「早く・・・」
インドリ(そうならそうともっと早く言ってくれればよかったのに。ドリィちゃんはツンデレなのかな?)
インドリィちゃん「足りない・・・」
インドリ(前にも同じこと言われたような・・・)
インドリィちゃん「早く起きなさい!レジスタが足りなぃ。」
インドリ「えっもうレジスタは実装したよ。」
インドリィちゃん「イベントにレジスタが足りないぃ。」
インドリ「何?何?何?何?何?何?何?何?。」(あたふた)
インドリィちゃん「この鳥頭!ReceivesDataEventにレジスタプロパティがないと、あとでレジスタに値を反映できないでしょうが!」
インドリ「ごめん。」(あたふた)
インドリィちゃん「さっさと実装しない。」
幽霊のような怖さを感じてインドリはあわてて、ReceivesMemoryDataEventArgsを実装した。
実装したコードは次の通りだ。
public class ReceivesMemoryDataEventArgs : MemoryAccessEventArgs
{
    private RegisterName target;

    public ReceivesMemoryDataEventArgs( 
        uint startAddress, byte dataLength, 
        ValueType value, RegisterName regName )
        : base( startAddress, dataLength, value ) 
    {
        this.target = regName;
    }
}
新しいイベントにすることも出来たが共通要素も多いし、まだ MemoryAccessEventArgsの実装が不完全だったので、インドリはひとまず継承することにした。
インドリ「さてリファクタリングするぞ」

鳥ゃーバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ

//メモリーからデータを受信する。

public event MemoryAccessEventHandler ReceivesDataEvent;

private void OnReceivesDataEvent( ReceivesMemoryDataEventArgs e ) 
{
    MemoryAccessEventHandler temp = SendDataEvent;
    if ( temp != null ) temp( this, e );
}

private void ReceivesData( 
    uint startAddress, byte dataLength, 
    ValueType value, RegisterName regName ) 
{
    MemoryAccessEventArgs e = 
        new MemoryAccessEventArgs( startAddress, dataLength, 
            value, regName );
    OnReceivesDataEvent( e );
}

インドリィちゃん「油断禁物。」
インドリ「ピぇー」

鳥ゃーバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ

インドリ「さっきのイベントの実装で、CPUがメモリデータを要求することは出来るけど、今のままじゃ受け取れないから、 まずはCPUオブジェクトのイベント登録だ!」
//自分を保持するPCを記憶する。
private Machine owner;
public Machine Owner {
    get { return owner; }
}

public ControlProcessingUnit( Machine owner ) {
    this.owner = owner;
    //自分を保有するマシンからメモリデータが送られてきた時の処理
    this.owner.SendDataEvent += 
        new Machine.MemoryAccessEventHandler
        ( ReceivesMemoryDataEvent ); 
}


インドリ「続いて前回作ったMachineオブジェクトもイベントを登録だ!」

public Machine( ) 
{
    this.cpu = new ControlProcessingUnit( this );
    //データがCPUから送られてきた事が一目でわかる名前にする
    this.cpu.SendDataEvent += 
        new ControlProcessingUnit.MemoryAccessEventHandler
        ( ReceivesMemoryData );

    //CPUがデータを要求していることが一目でわかる名前にする
    this.cpu.ReceivesDataEvent += 
        new ControlProcessingUnit.MemoryAccessEventHandler
        ( NotifyMemoryDataEvent );
}

インドリ「はぁはぁ・・・今回の記事やたら長いぞ。僕もう疲れたよ。」
インドリィちゃん「今日はもう休みなさい。」
インドリ「!!?インドリィちゃんが初めて優しい言葉を」
インドリィちゃん「うるさい!」
ドリィちゃんは素早くインドリの背後に回ると、首に手刀を振り落とした。
インドリ「きゅう!!」(バタ)
いいところだけど長すぎるから今回はおしまい。
ドリィちゃんは果たしてツンデレなのか?
そもそもインドリとドリィちゃんの関係は?
MOVE命令の実装は何時になったら終わるのか?
色々な謎を残したまま次回へ続く。

テーマ : プログラミング
ジャンル : コンピュータ

バイナリをつつく15-愛のメモリー

hello, みんな元気かい?
今日こそ僕はメモリアクセス機能を実装するぞ。
えっと・・・CPUオブジェクトにメモリーフィールドを宣言して・・・
インドリィちゃん「それキャッシュメモリ」
インドリ「え?何ドリィちゃん。」
インドリィちゃん「メモリはCPUの外!」
インドリ「そんな細かい事いいん」(途中でさえぎられる)
インドリィちゃん「いい加減なのは嫌い。」
インドリは何でそこまで現実を反映せねばならないのか大いに疑問だったが、何時もの様にドリィちゃんに押し切られた。

インドリ「うーん。あまり複雑化したくなかったけど、仕方がないMachineクラスを作るよ。もうやけだ。」
インドリィちゃん「当然よ。」
インドリ「Machineクラスにメモリフィールドをひとまずbyte配列で設定しよう。」
インドリィちゃん「今のところはそれで許してあげる。」
private byte[] memory;
インドリ「肝心なメモリへデータを転送したり、メモリのデータをレジスタに設定するのは・・・イベントにしよう。」
ということで、インドリはメモリに関するイベントを扱うためのオブジェクトを定義した。

public class MemoryAccessEventArgs : EventArgs
{

    //アクセスするメモリのアドレス
    private uint start;
    public uint StartAddress {
        get { return start; }
        set { start = value; }
    }

    //データの長さ(例:4バイト、2バイトなど)
    private byte length;
    public byte DataLength {
        get { return length; }
        set { length = value; }
    }

    //データの値
    private ValueType value;
    public ValueType Value {
        get { return this.value; }
        set { this.value = value; }
    }

    public MemoryAccessEventArgs( 
        uint startAddress, byte dataLength, ValueType value )
    {
        this.start = startAddress; 
        this.length = dataLength;
        this.value = value;
    }
}

インドリ「ふぅー。よし続けてCPUオブジェクトへイベントを実装だ。とぅー。」

バサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ


public delegate void MemoryAccessEventHandler
    ( object sender, TEventArgs e ) where TEventArgs : EventArgs;

//データを送るためのイベント
public event MemoryAccessEventHandler SendDataEvent;

private void OnSendDataEvent( MemoryAccessEventArgs e ) {
    MemoryAccessEventHandler temp = SendDataEvent;
    if ( temp != null ) temp( this, e );
}
private void SendData( uint startAddress, byte dataLength, ValueType value ) {
    MemoryAccessEventArgs e = 
        new MemoryAccessEventArgs( startAddress, dataLength, value );
    OnSendDataEvent( e );
}

//データを受信したい旨を伝えるイベント
public event MemoryAccessEventHandler ReceivesDataEvent;
private void OnReceivesDataEvent( MemoryAccessEventArgs e ) {
    MemoryAccessEventHandler temp = SendDataEvent;
    if ( temp != null ) temp( this, e );
}
    MemoryAccessEventArgs e = 
        new MemoryAccessEventArgs( startAddress, dataLength, value );
    OnSendDataEvent( e );
}


うりゃーバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ

インドリィちゃん「どうやってメモリデータをもらうの?」
鶴の一言で、さらにインドリはMachineクラスにも次のコードを実装した。

public delegate void MemoryAccessEventHandler
    ( object sender, TEventArgs e ) where TEventArgs : EventArgs;

//データを送るためのイベント
public event MemoryAccessEventHandler SendDataEvent;

private void OnSendDataEvent( MemoryAccessEventArgs e ) {
    MemoryAccessEventHandler temp = SendDataEvent;
    if ( temp != null ) temp( this, e );
}
private void SendData( uint startAddress, byte dataLength, ValueType value ) {
    MemoryAccessEventArgs e = 
        new MemoryAccessEventArgs( startAddress, dataLength, value );
    OnSendDataEvent( e );
}

あと他にも、互いにイベントの通知を受けるための作業と、実際にメモリ操作するためのプログラムが必要だが・・・
インドリ「頭使いすぎた、もうだめ・・・」(パタ)
インドリィちゃん「もぅ、だらしないわねぇ。」
インドリが倒れたから次回へ続く・・・
程々にご期待ください。
ではまた明日会いましょう。

テーマ : プログラミング
ジャンル : コンピュータ

バイナリをつつく14-コピペ鳥大いに困る

よーし今回は早速メモリをどう実装するか考えるぞ。
インドリィちゃん「その前に8ビットレジスタのテスト。」
インドリ「えぇ~盛り上がってたのに。間違っていないよ。」
インドリィちゃん「テ・ス・ト」
public void RegisterTo8BitTest( ) {
    //テスト用変数を準備
    byte val;
    ControlProcessingUnit target = new ControlProcessingUnit( );
    Random dom = new Random( ( int ) DateTime.Now.Ticks );
    Type type = target.GetType( );
    //リフレクション用変数を準備
    string[ ] names = new string[ ] { "AH", "AL", "BH", "BL",
        "CH", "CL", "DH", "DL"};
    BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.SetProperty | BindingFlags.GetProperty |
            BindingFlags.DeclaredOnly | BindingFlags.Instance;
    foreach ( string name in names ) {
        //テストするレジスタ用プロパティを取得
        PropertyInfo reg16 = type.GetProperty( 
            name.Substring( 0, 1 ) + 'X', flags );
        Assert.IsNotNull( reg16, "E" + name + "レジスタが足りないぃ~。" );
        PropertyInfo reg8 = type.GetProperty( name, flags );
        //値の取得&設定テスト
        Assert.IsNotNull( reg8, name + "レジスタが足りないぃ~。" );
        for ( int i = 0; i < 100; i++ ) {
            //共有フィールドを汚しておく
            reg16.SetValue( target, 
                ( ushort ) dom.Next( 0, short.MaxValue ), null );
            //8ビットレジスタのテスト
            val = ( byte ) dom.Next( byte.MinValue, byte.MaxValue );
            reg8.SetValue( target, val, null );
            Assert.AreEqual( val, reg8.GetValue( target, null ),
               name + "に「" + val.ToString( ) + 
                   "」を入れたら引っかかった。by ドリィ" );
        }
        //上下限チェック
        val = byte.MinValue;
        reg8.SetValue( target, val, null );
        Assert.AreEqual( val, reg8.GetValue( target, null ), 
            name + "の上限チェック失敗。by ドリィ" );
        val = byte.MaxValue;
        reg8.SetValue( target, val, null );
        Assert.AreEqual( val, reg8.GetValue( target, null ), 
            name + "下限チェック失敗。by ドリィ" );
    }
}

インドリ「う~なんか難しそう・・・あれ?よく見ると内容は16ビットの時と変わらないピヨ。多分大丈夫だ。」
インドリィちゃん「ふふ、それはどうかしら?」
インドリ「(やけに自信ありそうだな)よしテスト実行」

インドリ「あれ?!!!テスト失敗したぞ。何で?何で?・・・えーとAHが間違っているらしい。」
インドリィちゃん「ふふ、やっぱりね。」
インドリ「くぅ~なんか悔しいぃ~。」
インドリは長時間調べてようやく間違いを発見した。
ALレジスタはAXレジスタの先頭8ビットを返すが、 インドリは16ビットの時と同じロジックを使いまわししたので間違っていたのだ。
その間違いに気付いたインドリは数十分をかけてようやくAHレジスタの処理を修正した。
public byte AH {
    get {
        byte val = ( byte ) ( ( this.eax & 0x0000FF00 ) >> 8 );
        return val;
    }
    set {
        uint tmp = this.eax & 0xFFFF00FF;
        ushort hi = (ushort)(value << 8);
        this.value = tmp + hi;
    }
}

インドリ「ふぅー要約修正したピヨ。でもこれで大丈夫。」
インドリィちゃん「ふふふふ、コピペしている貴方がテストを通過できるかしら?」
インドリ「あれぇ?また違うレジスタで間違った。」
インドリィちゃん「同じロジックを共通化しないから。ふふふふ」
インドリ「あっそうか?」
インドリは大きな過ちを犯していることにようやく気付いた。 プログラムはコピペ(コピーと貼り付け)使いのインドリがおかした間違いを防ぐために、なるべく共通部分は纏めなくてはならないのだ。 その間違いに気付いたインドリは、レジスタ構造体を作って、 CPUオブジェクトの各レジスタ用プロパティが使用するフィールドを、uintからRegister構造体へ変えた。 ドリィちゃんの突っ込みを受けながらインドリが一生懸命書いたRegister構造体をみんなも見てみよう。
public struct Register
{
    private uint value;
    
    public uint ValueU32 {
        get { return this.value; }
        set { this.value = value; }
    }

    public ushort ValueU16 {
        get {
            ushort val = ( ushort ) ( this.value & 0x0000FFFF );
            return val;
        }
        set {
            uint tmp = this.value & 0xFFFF0000;
            this.value = tmp + value;
        }
    }

    public byte ValueHighByte {
        get {
            //16ビットの中の先頭8ビットを取り出す
            byte val = ( byte ) ( ( this.value & 0x0000FF00 ) >> 8 );
            return val;
        }
        set {
      //16ビットの中の先頭8ビットをリセットしてから足す
            uint tmp = this.value & 0xFFFF00FF;
            ushort hi = ( ushort ) ( value << 8 );
            this.value = tmp + hi;
        }
    }

    public byte ValueLowByte {
        get {
            byte val = ( byte ) ( this.value & 0x000000FF );
            return val;
        }
        set {
            uint tmp = this.value & 0xFFFFFF00;
            this.value = tmp + value;
        }
    }
}

CPUオブジェクトのプロパティは次のように変更した。

private Register eax;

public uint EAX {
    get { return this.eax.ValueU32; }
    set { this.eax.ValueU32 = value; }
}

public ushort AX {
    get { return this.eax.ValueU16; }
    set { this.eax.ValueU16 = value; }
}

public byte AH {
    get { return this.eax.ValueHighByte; }
    set { this.eax.ValueHighByte = value; }
}

public byte AL {
    get { return this.eax.ValueLowByte; }
    set { this.eax.ValueLowByte = value; }
}

こうしておけばつまらないミスも防げるし、将来64ビットレジスタを作成する際既存のコードをあまり修正しなくて済む。インドリの間違いは酷いけど、人間は誰しも完璧には作れないからこうやってリファクタリングをするのが大事だよ。
メモリの実装を紹介出来ないのは残念だけど今回はおしまい。
インドリィちゃん「ふふふふ、リファクタリング♪リファクタリング♪」

テーマ : プログラミング
ジャンル : コンピュータ

バイナリをつつく13-データが引越しします

バサバサバサお待たせーバサバサバサバサバサバサバサバサバサバサバサバサ
前回は命令の解析部分を詳しく説明したから、今回はついに命令の実行について説明するよ。
ひとまず大まかな流れをもう一度書くと・・・

            【命令実行】

  • 1、cmds変数から命令をひとつデキューします。
  • 2、デキューしたCommand変数の値から実行する命令を決定する。
  • 3、各命令文を実際に実行するメソッドを呼び出す。
    今回の例の場合void Move(Command cmd)

となっているからそんなに難しくないのがわかってもらえると思う。
えっと1は説明の必要がないから2を説明するよ。
実行する命令を選ぶのは何が必要だと思う?
それは・・・・・・・命令の名前だ。
じゃあ、命令の名前はどこにあったのかというと Command構造体CommandNameプロパティだよ。
ここまで判ればあとは簡単。いつものようにswitch文で判定するだけだ。
次に3の命令の実行だけど、これはどの命令でも同じで、Command構造体を渡すだけなんだ。だから今回の場合Command構造体の引数を受け取るMoveメソッドを用意するだけなんだ。 簡単だろ?そのために冗長な構造にしているんだ。

おまけに、データの転送を行うMov命令の動作自体も簡単だよ。ただ単にCPUオブジェクトのレジスタプロパティに値を代入するだけ。本当にこれだけなんだ。 一見難しそうに見えるCPUのシュミレートも工夫次第で案外単純な処理に分解できるんだ。難しいことを難しいままやっても混乱するだけだからこれは重要な事だよ。難しい分野にトライする時は簡単に考える事からはじめるといいピヨ。効率を追及するのは後からの方がいいんだ。
次回はメモリの扱い方を突付くよ。どんどん完成に近づいているから楽しみにしてね。
じゃあねー、とぉー。

テーマ : プログラミング
ジャンル : コンピュータ

バイナリをつつく12-逆さ星人の考え方

バサバサバサバサバサバサバサバサバサバサバサバサインドリ入りまーす。
前回は大まかな流れを説明したから今回は細部を書くよ。
だけど、命令の読み込みについては特に説明する事ないからそれは飛ばして、 【命令の意味解析】を説明するピヨ。
まずCommand構造体を見てみよう。

現時点のCommand構造体
  • CommandName列挙体型のNameプロパティ。
  • NextInfoType列挙体型のNextTypeプロパティ。
  • Register列挙体型のDestinationRegisterプロパティ。
  • uing型(UInt32)のSourceValueプロパティ。
みんなが気になるのはSourceValueプロパティの型が何で符号なし32ビット値なのかだと思う。
それは、命令には符号を考慮するもの符号を考慮するしないものがあるからなんだ。 だからもし、このプロパティの型をint(Int32)としてしまったら、符合を考慮しない値を格納できなくなってしまう。だからひとまず、 uing型に入れといて命令の実行の際に符号の扱いを決める仕様にしたんだ。
さて、次に意外と思うだろうけど前回の記事の「先ほど取り出した値をCommand構造体に設定します。」ここの部分ピヨ。 何故注意が必要かというと、IntelCPUはリトルエンディアン:little endian(小さなインデアンじゃないよ)でデータを扱うから、 逆に送られてくる数値を元に戻さないといけないからなんだ。
ややこしいと思うから実例を挙げて説明するよ。

「NASMアセンブラが出力したリスティングファイルから抜粋」
00000000 B805F69D07 mov EAX, 127792645

何かおかしいことに気付いたかな?B8はmov命令の数値だから残りを関数電卓で算出してみると結果は100048135。????!あれぇーどうなっているのかな?じゃあ今度は127792645を16進表記にすると079DF605 これをピヨーク見てみよう1バイトずつ逆になっているのがわかると思う。
これを読んでいる人の中には何でやねん!と思わず呟いた人もいると思うけど、残念ながら僕にもわからない。多分Intelさんの大人の事情なんだろうね。 それでCPUオブジェクトを実装するのにには数値を逆にするメソッドが必要になる。そこで次のメソッドを実装した。

public static uint ConvertBigEndian( byte[ ] values ) {
    if ( values.Length != 4 )
        throw new ArgumentOutOfRangeException( 
            "このメソッドには必ず4バイトの値を渡して下さい。" );
        uint rVal = ( uint ) values[ 0 ];
        rVal += ( uint ) values[ 1 ] << 8;
        rVal += ( uint ) values[ 2 ] << 16;
        rVal += ( uint ) values[ 3 ] << 24;
        return rVal;
}
インドリィちゃん「テストファースト。」
インドリ「うああ来たー!」
public void ConvertBigEndianTest( ) { 
    //32ビットの一番左が0
    uint rightVal = 127792645; //0x07 0x9D 0xF6 0x05
    byte[ ] vals = new byte[ ] { 0x05, 0xF6, 0x9D, 0x07 };
    Assert.AreEqual( rightVal, 
        ControlProcessingUnit.ConvertBigEndian(vals), 
        "32ビット目が【0】の値でミス。 by ドリィ" );

    //32ビットの一番左が1
    rightVal = 3257141518; 
    vals = new byte[ ] { 0x0E, 0x09, 0x24, 0xC2 };
    Assert.AreEqual( rightVal,
        ControlProcessingUnit.ConvertBigEndian( vals ),
        "32ビット目が【1】の値でミス。 by ドリィ" );
}
インドリ「やったー成功したぞ!」
インドリィちゃん「トリのくせに・・・かわいくない。ぐっすん。」

インドリ「あわわわ。どうしよう。1回ぐらいテストに失敗したほうがよかったかな?」
まぁ何はともあれ、命令の意味解析で重要な事は書いたから今回の記事はこれで終わるよ。 じゃあねー。

テーマ : プログラミング
ジャンル : コンピュータ

バイナリをつつく11-任務実行の概要

さあ、いよいよ命令を実際に実行するまでするピヨ。 話しの流れからいって、大概の人はADD命令を実装すると考えると思うけど、 残念ながらその前にレジスタを初期化しなくてはならないんだ。 そうしないと、どんなことが起こるかわからないんだ。だから今回はMOV命令(move EAX 即値)を実装するピヨ。
この命令を実装するには、CPUが命令を実行する流れをふまえなくちゃならない。
だからCPUが命令を実行する手順とそれに対応するメソッドをおさらいしよう。
1、マシン語で書かれた命令を読み込む=void ReadCommand(Byte[] code)
2、命令の意味を解析する=void AnalyzeCommand()
3、ついに命令を実行=void ExecuteCommand()
みんな準備はいいピヨね?さあ、ドキドキワクワクする実装にトライだー。
※これから説明する手順はmov EAX 32ビット値の手順です。

バサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ

            【命令読み込み】

この動作は命令を読み込むだけなので簡単だ。
  • CPUオブジェクト内でQueue<byte> datasフィールドを宣言する。
  • コンストラクタ内でdatas変数を初期化する。
  • ReadCommandメソッド内で
    foreach(byte data in code) datas.Enqueue(data);
とすればいい(今のところは)
※ListじゃなくてQueueにしたわけは、そのほうが実際のCPUの動作に近いと判断したからです。

バサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ
            【命令の意味解析】

  • CPUオブジェクト内でQueue<Command> cmdsフィールドを宣言する。
  • コンストラクタ内でcmds変数を初期化する。
※これ以降はvoid AnalyzeCommand()メソッド内の手順となります。
  • datas変数に保持している値をデキューして、その値を引数としてSearchOpeCodeメソッドを呼び出します。
  • 命令を表すCommand構造体を初期化します。
  • SearchOpeCodeメソッドの戻り値からディスティネーション・レジスタを確定し、Command構造体のDestinationRegisterプロパティに代入します。
  • datas変数から4回(32ビットだから)デキューします。
  • 値を32ビットの値へ変換します。
  • 先ほど取り出した値をCommand構造体に設定します。
  • cmds変数に先ほど生成したCommand構造体のインスタンスをエンキューします。

バサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサバサ
            【命令実行】

  • cmds変数から命令をひとつデキューします。
  • デキューしたCommand変数の値から実行する命令を決定する。
  • 各命令文を実際に実行するメソッドを呼び出す。
    今回の例の場合void Move(Command cmd)


はぁはぁはぁ、今回は一気にやったので疲れたよ。駆け足でひとまず概要を紹介したからわかりにくかったと思う。 だから次回からはそれぞれの手順を詳しく説明していくピヨ。楽しみにしててね。じゃぁ、また会おう♪

テーマ : プログラミング
ジャンル : コンピュータ

バイナリをつつく10ー電波ソムリエ2

コンニチハ コンニチハ
今回はレジスタ以外の情報を必要とする命令を一緒に調べていこう。 いつものマニュアルのA-61バイトのオペコード・マップ(左側)をざっと眺めると EbとかGbなどの謎の記号が登場する。これは一体何なんだろう。
マニュアルを調べると、A-1~A3にその正体が書いてあった。ピムピム。 大文字はアドレス指定方式で 小文字のほうはオペランド・タイプのコードらしい。 それにしても多い・・・めまいがするピヨ。 でもよく考えてみれば当たり前かもしれないピヨね。だって、CPUは1と0しか判らないから、レジスタやメモリとかも1と0で判断しなければならない。 人間に例えると、ピとヨだけで会話するようなものなんだね。CPUって案外馬鹿っぽい・・・
それはさておき、何かの命令を前回用意したSearchOpeCodeメソッド内に用意しよう。
ピヨっとその前に、アドレス指定方式をAddressingMode列挙体に、 オペランド・タイプをOperandType列挙体で実装しておこう。
これでよし!次にめぼしい命令を探すととしよう。
バッサ
バッサ
バッサ
みーつけた。前回の例で使ったADDにしよう。
実装の抜粋
public static OpeCodeInfo SearchOpeCode( byte value ) {
    OpeCodeInfo info = new OpeCodeInfo( value ); //オペコード・マップの情報
    byte row = ( byte ) ( value & 0x11110000 ); //バイトから行を抽出
    byte col = ( byte ) ( value & 0x00001111 ); //バイトから列を抽出
    //途中省略
    switch ( row ) {
	case 0:
            switch ( col ) {
	        #region ADD
            	case 0:
                    info.Name = CommandName.ADD;
                    info.NextType = NextInfoType.DoubleAddressingMode;
                    info.DestinationAddressMode = AddressingMode.E;
                    info.DestinationOperand = OperandType.b;
                    info.SourceAddressMode = AddressingMode.G;
                    info.SourceOperand = OperandType.b;
                    break; 
//途中省略
}

ピッし、これで大方の命令の情報を設定できるぞ。きりがいいので今回の記事はこれで終わるピヨ。 これからどんどん面白くなるからまた来てね。

テーマ : プログラミング
ジャンル : コンピュータ

プロフィール

インドリ

Author:インドリ
みなさん、はじめまして、
コンニチハ。

ボクは、無限の夢(infinity dream)を持つネタ好きな虹色の鳥インドリ(in dre)です。
色々な情報処理技術を啄ばむから楽しみにしてね。

http://twitter.com/indori
は別人による嫌がらせ行為です。
私とは関係ないので注意して下さい。
次はなりすましブログなどをするかもしれませんが、ここ以外でブログをするつもりがないので、ここ以外にインドリのブログがあったとしても無視してください。


何度言っても分からない人がいるので、ここにコメント欄へ書き込むときの注意事項を書きます。


一、社会人としてのマナーをわきまえましょう。
一、妄想に基づく書き込みを止めてください。
一、暴言の類は書かないで下さい。
一、某誹謗中傷サイトの書き込みは彼らの妄想に基づく書き込みですから無視して、ここへ書き込まないで下さい。
一、コメント書く前に他のコメントよく読んでから行って下さい。
一、言いがかかり等の行為を禁止します。
一、その他常識的に考えて迷惑なコメントはしないで下さい。


以上のルールを守れない人のコメントは削除します。



利用上の注意
ここに紹介してある文章およびプログラムコードは正確であるように心がけておりますが、内容を保証するものではありません。当サイトの内容によって生じた損害については、一切の責任を負いませんので御了承ください。


執筆したCodeZineの記事


【VB.NETで仮想CPUを作ろう】

  1. VB.NETで仮想CPUを作ろう
  2. レジスタの実装
  3. 仮想CPUのGUI化
  4. テストドライバの改良
  5. CPUの基礎動作の実装
  6. MOV命令の実装
  7. ADD命令実装
  8. SUB命令実装
  9. INC命令&DEC命令の実装と命令長
  10. MLU命令の実装とModR/Mについて
  11. DIV命令の実装とイベント設計について
  12. 機械語駆動式 関数電卓を作ろう!
  13. 機械語駆動式 関数電卓を作ろう! 解答編(前半)
  14. 機械語駆動式 関数電卓を作ろう! 解答編(後半)


【仮想ネットワーク実装でTCP/IPを学ぼう】
  1. TCP/IPの基礎と勘所
  2. ネットワークアクセス層の勘所
  3. インターネット層の勘所
  4. トランスポート層の勘所
  5. アプリケーション層の勘所
  6. セキュリティの基礎と仮想ネットワークの仕様
  7. GDI+と独自プロトコルの定義



【並列化】
インテル Parallel Studioを使って並列化プログラミングを試してみた
並列プログラミングの効率的なデバッグを実現する「Parallel Inspector」


【TBBシリーズ】
  1. インテル スレッディング・ビルディング・ブロックの概要
  2. インテルTBBから学ぶループの並列化
  3. スレッドセーフとインテルTBBのコンテナ
  4. インテルTBBのスレッドクラス


【OpenMPシリーズ】
  1. OpenMPの基礎構文
  2. OpenMPの実行時ライブラリと並列ループ
  3. OpenMPのメモリモデルとfork- joinモデル

最近の記事
最近のコメント
月別アーカイブ
カテゴリ
Ada (9)
COBOL (5)
C (9)
C++ (11)
C# (370)
D (25)
Java (8)
Perl (1)
Ruby (14)
PHP (2)
Boo (2)
Cobra (2)
LISP (6)
F# (33)
HTML (0)
XHTML (0)
CSS (0)
XML (0)
XSLT (0)
Scala (4)
WPF (0)
WF (2)
WCF (0)
LINQ (4)
MONO (5)
Linux (0)
MySQL (0)
ブログ内検索
リンク
最近のトラックバック
RSSフィード
ブロとも申請フォーム

この人とブロともになる

QRコード
FC2カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。