- ベストアンサー
ret とretfについて。
アセンブリ言語では、retとretfとはどういう事でしょうか?nearリターンとfarリターンの違いは何でしょうか?教えていただけないでしょうか?すみません。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
あ、ミス#2さん指摘ありがと。 x86系は、PCじゃなくてIP=インストラクションポインタですね^^ こちら、80系出身なもので(んま、asm職人にはどっちでも通じますが^^)一応ミスはミス。
その他の回答 (3)
- AsarKingChang
- ベストアンサー率46% (3467/7474)
>直訳するとセグメントは分割ですが、nearは現在の分割に戻る。farは、分割も復元して戻る。という意味になると思うのですが、どういう事でしょうか?差し支えなければ教えていただけないでしょうか? すみません。 英語的にも、認識としてもそれであってますよ。 32ビットだと、話が面倒なので16ビットモードで説明しますが。 call 0x1234 これは、今いるセグメント内の「1234番地をコールしますよね」 しかし、16ビットは、65536バイトしかありません。 初代8086は、1MByteのメモリを搭載可能でした。 しかし、16ビットでは、1MBをアクセスできないのですよ。 なので、セグメントという「区画わけ=部屋分け」=つまりは分割 する概念が入りました。 同じ、65536=64KB内にある、関数や番地は「Near」と呼ばれ 直接値を書けばジャンプなりコールできます。 しかし「それより外はどうするの?」 ということになり、16バイトを1つと数える セグメントが導入されました。 0000:0000=これは、0番地 0000:0010=これは、16番地こっちはNear 0001:0000=これも、16番地これはFarになる。 で、16ビットx16を1として数える=65536x16=1MB になったのです。 なので、 call near 0x1234 これは、SS:SPレジスタに「次の番地をスタックに入れて0x1234番地にジャンプ」する命令 ret スタックから1つの値を取り出す=これが、「次の番地」 そこにジャンプする。 となります。 今度は、 call far 0x1234:0x5678 この場合はSS:SPレジスタ位置に「現在のCSと次の番地を入れてから、CS=1234をセットした上で、0x5678へジャンプ」する命令になります。 なぜ、retが2つあるのかは、これでわかったと思いますが。 call far の後、retすると、上の例では、本来の位置ではなく、 CS=1234のままで、この命令の次の番地の「下位16ビットに飛んでしまいます」 これを避けるための命令がretf ってことです。 なお、ジャンプ命令と、コール命令をごちゃごちゃに書いてますが。 実は「同じ」です。 mov ax,offset next push ax jmp function :function ret このRET命令がaxに入れていたnextの番地を復元してそこにジャンプ :next ここにきます。 要するに、callとjmp命令は、スタックを使うか使わないかだけで、 同じ物なんです。 なお、スタックというのは、SP(スタックポインタ)ですが、 これも、16ビットなので、当然64Kしかないので、 これにもSS(スタックセグメント) を補うことで、1MB空間を表せるように拡張されています。 CS:PC CS=コードセグメント:PC=プログラムカウンタ これが、プログラム用の、セグメント データは、 DS:データセグメント(何もしなければ常にDSを指してます) と、用途ごとにセグメントが複数あるのもこのためです。 そうしないと、 「今いるセグメントのデータしか読み書きできない」為です。 特殊なもので、用途が決まっていないESなどもあります。 これらは、 mov ax,[0x1234] これは、DSセグメント内の0x1234番地にあるデータを16ビット取り出せ!の命令 mov ax,es:[0x1234] これは、ESセグメント内の0x1234番地にあるデータを16ビット取り出せ!の命令 (これをセグメントオーバーライドと言います) それが「さらに複雑になってページという扱いになってる」 286以降のセグメントレジスタです。 なので、説明したくないほど面倒です。 なので、勉強が目的なら、リアルモード(リニア)86で 止めておく方が、いいかと。
- chie65536(@chie65535)
- ベストアンサー率44% (8741/19839)
x86系CPUは、レジスタが16ビットなので、アドレスも16ビットで記憶しています。 16ビットだと、表現できるのは「0000~FFFF」の範囲だけなので、メモリ空間は64キロバイトまでしか表現できません。 しかし、x86系CPUのアドレスバスは20ビットなので、20ビットのアドレスを、16ビットのレジスタ2つの組み合わせで表す事にしました(実アドレス空間は0x00000~0xFFFFFまでの1メガバイトです) 具体的には「セグメントレジスタ×16+オフセットレジスタ」という計算で、20ビットの実アドレスを表現しています。 この時、16倍される上位の方を「セグメント」、そのまま足される下位の方を「オフセット」と言います。 つまり、1メガバイトのアドレス空間を「64キロバイトごとの空間」に分割して、表現しているのです。この「分割」の上位アドレスを表すのが「分割」を意味する「セグメント」なのです。 例えば、セグメント0x1F53、オフセット0x0106の場合、実アドレスは0x1F636になります。 X86系CPUの「プログラムカウンタ(今、どこのメモリのプログラムを実行しているかを覚えているレジスタ)は、セグメントの方はCSレジスタ、オフセットの方はIPレジスタが保持しています。 RET命令を実行すると、CPUは「戻り場所を記憶しているメモリ」から16ビット分のデータを取り出して、それをIPレジスタに代入して、IPレジスタの指す場所から実行を再開します。 RETF命令を実行すると、CPUは「戻り場所を記憶しているメモリ」から16ビット分のデータ2つを取り出して、それをCSレジスタとIPレジスタの2つに代入して、CSとIPレジスタの指す場所から実行を再開します。 なので、サブルーチンを呼び出す際は、呼び出されるサブルーチンがRETで終わっているかRETFで終わっているかを意識して、NEAR CALLで呼ぶべきかなのかFAR CALLで呼ぶべきなのかを明確にしないといけません。 RETで終わってるサブルーチンはNEARで呼ぶ、RETFで終わっているサブルーチンはFARで呼ぶ必要があります。間違うと、帰るアドレスが違ってしまい、プログラムが暴走します。
お礼
RET命令を実行すると、CPUは「戻り場所を記憶しているメモリ」から16ビット分のデータを取り出して、それをIPレジスタに代入して、IPレジスタの指す場所から実行を再開します。 RETF命令を実行すると、CPUは「戻り場所を記憶しているメモリ」から16ビット分のデータ2つを取り出して、それをCSレジスタとIPレジスタの2つに代入して、CSとIPレジスタの指す場所から実行を再開します。 ここを詳しく教えていただけないでしょうか?すみません。
補足
しかしから詳しく教えていただけないでしょうか?すみません。
- AsarKingChang
- ベストアンサー率46% (3467/7474)
https://okwave.jp/qa/q9895187.html この板で、これ回答済みですが、 https://www.felixcloutier.com/x86/ret 説明文に、 Near return — A return to a calling procedure within the current code segment (the segment currently pointed to by the CS register), sometimes referred to as an intrasegment return. Far return — A return to a calling procedure located in a different segment than the current code segment, sometimes referred to as an intersegment return. と書いてますよ。 nearは現在のセグメント内で戻る。 farの方は、セグメントも復元して戻る。 の違いだと。 なお、CSってのが、CodeSegmentレジスタの事です。 あと、書くときIA32やx86と必ず書いた方がいいです。 世の中にIntel以外のCPUもたくさんあり、 当然、機械語も全部違うので、言語を特定しなければ 明後日の回答が付くことは避けられませんので。 さらに面倒なのが、x86はモードごとに命令が変わるので、 一つのCPUですら、命令が違うという問題も持っているので、 今は、32ビット以上のIntelCPUをアセンブラで書く人は ほぼいないですよ。 勉強したいならx86(リアルモード)が一番、お勧めですね。 https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%83%86%E3%82%AF%E3%83%88%E3%83%A2%E3%83%BC%E3%83%89 今説明に使ってるのは、「386のプロテクトモード」 の命令形ではあります。 IntelのCPUって上位互換=つまり、過去の命令も実行できる関係で、 相当複雑なので、勉強するには、一番最悪なCPUですんで。 勉強が目的ならRISC系の方がいいかとは思いますけどね。 んま、後は頑張って!
補足
nearは現在のセグメント内で戻る。 farの方は、セグメントも復元して戻る。 の違いだと。 直訳するとセグメントは分割ですが、nearは現在の分割に戻る。farは、分割も復元して戻る。という意味になると思うのですが、どういう事でしょうか?差し支えなければ教えていただけないでしょうか?すみません。
補足
しかしからもう少し詳しく教えていただけないでしょうか?すみません。