自作エミュレータで学ぶx86アーキテクチャ 3.7 leave

https://github.com/mas454/x86emu

(define (leave emu)
  (let ([ebp (get-register32 emu EBP)])
    (set-register32 emu ESP ebp)
    (set-register32 emu EBP (pop32 emu))
    (eip-add emu 1)))

 leaveはleave命令に対応している関数です。
leaveはスタックフレームを破棄するのに使う命令なのですが、
やっていることは単純で

mov esp,ebp
pop ebp

のようなプログラムを実行しています。

 スタックフレームに関しては、「コンピュータシステムの理論と実装」を
読むのをおすすめします。
スタックフレームはコンパイラの処理も重要だと思うので、
cpuだけじゃなくコンパイラの実装もするこっちの本も読むと、より詳しく理解できると思います。

自作エミュレータで学ぶx86アーキテクチャ 3.7 call_rel32,ret

 call-rel32とretはcall命令とret命令に対応した関数です。

(define (call-rel32 emu)
  (let1 diff (get-sign-code32 emu 1)
    (push32 emu (+ (ref emu 'eip) 5))
    (eip-add emu (+ diff 5))))

(define (ret emu)
  (set! (ref emu 'eip) (pop32 emu)))

 call-rel32はcallの次の命令の番地を基準にして前後32ビットの範囲でジャンプできる
命令です。
例えば、以下のようなプログラムだと

call hoge
mov eax,0x0000

「mov eax,0x0000」がある番地を基準にして前後32ビットの範囲でジャンプできます。

 call-rel32の以下の部分で

(eip-add emu (+ diff 5))))

5を足しているのは、call命令の全体が5バイトだからです。
例えば上の「call hoge」のある番地に5を足すと、基準になる「mov eax,0x0000」の
番地になります。

(push32 emu (+ (ref emu 'eip) 5))

 この部分はcall命令を実行した後に戻ってくる番地をプッシュしています。
ここで5を足しているのも上の理由とだいたい同じです。

 retでは、ここでプッシュした値をポップしてeipに設定しています。

(set! (ref emu 'eip) (pop32 emu)))

プログラムの全体はgithubに置いています。
https://github.com/mas454/x86emu

自作エミュレータで学ぶx86アーキテクチャ 3.7 push_r32、pop_r32

 push-r32とpop-r32は、push命令とpop命令に対応した関数です。

(define (push-r32 emu)
  (let1 reg (- (get-code8 emu 0) #x50) 
    (push32 emu (get-register32 emu reg))
    (eip-add emu 1)))

(define (pop-r32 emu)
  (let1 reg (- (get-code8 emu 0) #x58)
    (set-register32 emu reg (pop32 emu))
    (eip-add emu 1)))
 (let1 reg (- (get-code8 emu 0) #x50)

 この部分はメモリから1バイト読んで、対応するレジスタの番号を
得ています。
 例えば0ならeax、1ならecxという感じです。

 (let1 reg (- (get-code8 emu 0) #x58)

 この部分も同じようにレジスタの番号を得ています。

 全体のコードはGitHubにあります。
https://github.com/mas454/x86emu

自作エミュレータで学ぶx86アーキテクチャ 3.7 push32,pop32

(define (push32 emu value)
  (let* ([esp (get-register-number 'esp)]
	 [address (- (get-register32 emu esp) 4)])
    (set-register32 emu esp address)
    (set-memory32 emu address value)))

(define (pop32 emu)
  (let* ([esp (get-register-number 'esp)]
	 [address (get-register32 emu esp)]
	 [ret (get-memory32 emu address)])
    (set-register32 emu esp (+ address 4))
    ret))

自作エミュレータで学ぶx86アーキテクチャ 3.6メモ

「call メモリ番地」で現在のeipの値をスタックにプッシュし、ジャンプ先のメモリ番地をeipに
書き込む。

 retはcallと対にして使う。
 retはスタックから4バイトの値をポップし、その値をeipにセットする。

レジスタは少なくて引数には適さないが、戻り値は1つなのでレジスタを使う。

 戻り値に使われるレジスタはeaxに決まっている。

自作エミュレータで学ぶx86アーキテクチャ 3.5

メモ:3.5 無条件分岐命令

 jmpはeipに値を入れるだけでなく、高速化のために先読みした命令を
捨てるという役割がある。

 cpuは高速化のために「パイプライン」という仕組みを持っている。

 「パイプライン」は、ある命令を実行するときにはすでに何命令も
先の命令をフェッチしたりデコードしたりしている。

 jmpでeipの値が変わるので先読みしていたものを捨てなければならない。
これを「パイプラインのフラッシュ」などと呼ぶ。