「みんな教えて! 選手権!!」開催のお知らせ

C言語で書かれたプログラムの中に、アセンブラで書かれた関数を使うため、インラインアセンブラで関数を作っていたのですが、私の能力の限界を感じたので、是非、ご教授願います。以下に示します。

元のアセンブラの関数write_mem82(int addr, int data)

MOV EAX, [ESP+4]
MOV AL, [ESP+8]
MOV [EAX], AL
RET

です。これは、OS○作○門という本に載っていたものですが、プログラムをインラインアセンブラにすると、成功するのかふと疑問に思ったのです。よって、アセンブラの種類は、nasmを基にしたnaskです。

こういうことは、その本のサポートページか何かで質問すればよい的なことをおっしゃる方もおられると思います。残念ながら、サポートページは、ほぼ凍結状態で、何年待てば回答が返ってくるのか?という状態です。

そういう経歴で、ここの質問させていただくに至りました。

肝心の、私が書いてコンパイルエラーになるプログラムを書きます。

static __inline__ void write_mem82(int addr, int data){
__asm__ (
"MOV EAX,[ESP+4]":
"MOV AL,[ESP+8]"
"MOV [EAX],AL"
"RET"
);

}
です。"MOV [EAX],AL"でエラーが出ます内容は構文が間違っているという内容のものです。なお、関数の名前は、意図的に変えてあります。オリジナルとは違います。

大した関数ではないのかもしれないのですが、わからないのでよろしくおねがいいたします。

A 回答 (13件中1~10件)

gcc/gasの流儀に従って書くならこうじゃないでしょうか。



void write_mem82(int addr, int data) {
char data8 = (char)data;
__asm__(
"movb%1, (%0)\n\t"
: : "r"(addr), "r"(data8)
);
}

「GCC インラインアセンブラ」でググれば情報は見つかります。

この回答への補足

回答くださりありがとうございます。結果から申しますと。無事動きました。!!エラーもありません。というか、私もこんなコードをかけるように勉強したいです。検索のキーワードをあげてくださっていますが、私が気付いていない可能性が高いので、お勧めのサイトがあれば、(書籍でもいいです。)紹介していただきたいのですが、お願いできますでしょうか。(大げさかもしれませんが、本心では弟子にしてほしいぐらいです。感動しました。でもさっぱり分かっていないように思います。)実際にコンパイルしたソースと実行結果のログを載せておきます。ありがとうございました。

bootpack.c --------------------------


static __inline__ void io_hlt2(void){
__asm __volatile (
"HLT\n\t"
"RET"

);

}

void write_mem82(int addr, int data) {
char data8 = (char)data;
__asm__(
"movb%1, (%0)\n\t"
: : "r"(addr), "r"(data8)
);
}
void HariMain(void)
{
int i;

for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem82(i, i & 0x0f);
}
for (;;) {
io_hlt2();
}

}
結果のログーーーーーーーーーーーーーーーーーーーーーーーー



C:\TEST\test>make run

C:\TEST\test>..\z_tools\make.exe run
../z_tools/make.exe -r img
make.exe[1]: Entering directory `C:/TEST/test'
../z_tools/make.exe -r haribote.img
make.exe[2]: Entering directory `C:/TEST/test'
make.exe[2]: `haribote.img' is up to date.
make.exe[2]: Leaving directory `C:/TEST/test'
make.exe[1]: Leaving directory `C:/TEST/test'
copy haribote.img ..\z_tools\qemu\fdimage0.bin
1 個のファイルをコピーしました。
../z_tools/make.exe -r -C ../z_tools/qemu
make.exe[1]: Entering directory `C:/TEST/z_tools/qemu'
qemu.exe -L . -m 32 -localtime -std-vga -fda fdimage0.bin
make.exe[1]: Leaving directory `C:/TEST/z_tools/qemu'

C:\TEST\test>
ここまでーーーーーーーーーーーーー


回答いただくまで作っていたプログラムがあるのですが、エラーが1つだけありHLTをスキップしてしまうものでした。ついでに載せておきます。

bootpack.c------------------------ここから

asm("io_hlt2:");
asm("haltl");
asm("ret");
extern void io_hlt2(void);

asm("write_mem82:");
asm("movl %ecx, [%esp+4]");
asm("movl %al, [%esp+8]");
asm("movl [%ecx], %al");
asm("ret");
extern void write_mem82(int addr, int data);


void HariMain(void)
{
int i;

for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem82(i, i & 0x0f);
}
for (;;) {
io_hlt2();
}

}

ここまでーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

よろしくおねがいいたします。

補足日時:2012/09/04 22:49
    • good
    • 0

なぜインラインアセンブラ中にretを書いたらいけないかですがサンプルのソースを見た方がわかりやすいかと思います。


FreeBSDのgccでのコンパイルなので本当に参考程度ですが。

% cat halt.c
void
io_hlt2(void)
{
__asm__ __volatile__(
"hlt\n\t"
"ret"
);
}

% cc -m32 -O2 -S halt.c
% cat halt.s
.file "halt.c"
.text
.p2align 4,,15
.globl io_hlt2
.type io_hlt2, @function
io_hlt2:
pushl %ebp
movl %esp, %ebp
#APP
hlt
ret
#NO_APP
popl %ebp
ret
.size io_hlt2, .-io_hlt2
.ident "GCC: (GNU) 4.2.1 20070831 patched [FreeBSD]"
.section .note.GNU-stack,"",@progbits
%

ラベルのio_hlt2:からpopl %ebpの次のretまでがio_hlt2関数で、#APP~#NO_APPの間がインラインアセンブラの出力です。
上記の例だとpopl %ebpしてからretすべきなのに、インラインアセンブラ中でそれをせずにretしてしまってます。
またコンパイラが用意しているretも無視することになります。
popl %ebpに関してはコンパイルオプションによりする必要のないケースもありますが、それを期待したコードにすべきでもありません。
    • good
    • 0

#7の補足で解決はしてる?んですね。



>static __inline__ void io_hlt2(void){
>__asm __volatile (
>"HLT\n\t"
>"RET"

>);

>}

はHLTがスキップされずに

>static __inline__ void io_hlt2(void){
>__asm__ (
>"HLT"
>);

>}

はHLTがスキップされるというのはわかりませんが。

__asm__ __volatile__("HLT");

の方がいいですけど、HLTがスキップされるのとは関係ないでしょうし・・・
retは書いてはいけないのは確かなんだけどなぁ・・・(コンパイルできても実行としてはおかしいはず)
    • good
    • 0

もしかしたら"HLT"は"hlt"と書かないといけないのかも。

    • good
    • 0

>でも教えてくださったコードで、なぜかエラーが出ます。

インラインアセンブラを使わない場合のものも、載せておきます。

エラーメッセージちゃんと読んでますか?
「エラーを訳したりせずに、そのまま書きましょう。」とは書きましたが、あなた自身が読む必要はないなどとはいってませんけど。
どう見ても

>./z_tools/gas2nask.exe -a bootpack.gas bootpack.nas
>skip:HLT

スキップされてるのはio_hlt2()のHLTです(私はwrite_mem82()のコードしか書いてない)

>static __inline__ void io_hlt2(void){
>__asm__ (
>"HLT"
>);

>}

もしかしてですが、gas2nask.exeがhltには対応してないんじゃないでしょうか。
またはhltではなく別の表記とか。

>個人的にはインラインアセンブラを使わない方法に、興味があるのですが、一体どんな勉強をすればよいの>か、教えていただけるとありがたいです。

ふつうにC言語の勉強をすればよいだけかと。

>回答くださりありがとうございます。正直なところ、耳が痛いです。苦言ごもっともだと思います。しかし、恵まれた環境におられる方ならば、わざわざここまで質問をしないと思います。すぐ近くに
(以下略)

ご自分が恵まれた環境にないとおっしゃられてるようですけど、とっても恵まれてる方だと思いますよ。
インターネットで検索して調べるなんてことができるわけですから。
近くに聞く人がいないとかは些細なこと。
    • good
    • 0

>よろしくおねがいいたします。

私もがんばります。

「も」じゃないです。
一番がんばらないといけないのは、あなたです。

回答者ががんばってくれるかどうかは回答者次第。

この回答への補足

回答くださりありがとうございます。正直なところ、耳が痛いです。苦言ごもっともだと思います。しかし、恵まれた環境におられる方ならば、わざわざここまで質問をしないと思います。すぐ近くにいる誰かに聞けばよいわけですから。でも、そうじゃない人間もこうしてここにいるわけですから、私の表現が悪かったのだと思いますが、がんばる材料を、皆さま方いただいているという意味で、感謝いたします。

補足日時:2012/09/04 23:22
    • good
    • 0

#7の方が既に書いていらっしゃいますが、ログ中の



>../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas

は、gas形式のアセンブラソースからnasm形式のアセンブラソースへコンバートしてるようなので
gccのインラインアセンブラで書くのはgas形式だと思います。
で、書くならたぶん

static __inline__ void write_mem82(int addr, int data){
__asm__("movb %1,%0": "=m"(*(char *)addr): "r"((char)data): "memory");
}

だと思います。
で、実はこれインラインアセンブラ使う必要がなくて

static __inline__ void write_mem82(int addr, int data){
*(char *)addr = (char)data;
}

で済むはずです。

#1でも既に書いてることもありますが
・[ESP+4]がaddr,[ESP+8]がdataのようなスタックレジスタ相対などの決めうちは×
・インラインアセンブラでretを書くのは× (例えば例で書かれているmain関数でwrite_mem82()がインライン展開されたらいっしょにretも展開されます)
・インラインアセンブラ内では好き勝手にレジスタは使用できず使えるものは決まってます(この辺はCコンパイラのマニュアルなど読んでください)
・C言語は高級アセンブラともいわれるくらいなのでアセンブラでできるほとんどの事はC言語でもインラインアセンブラを使わずに書けます。インラインアセンブラはC言語で書けないような事を書くときだけにしておいた方がいいです。
・C言語がどのようなコードを出力するのか想像できないのであればインラインアセンブラは使わない方がいいです。最適化のつもりがコンパイラのする最適化の足をひっぱったりします。

この回答への補足

回答くださりありがとうございます。解説がわかりやすくて助かります。でも教えてくださったコードで、なぜかエラーが出ます。インラインアセンブラを使わない場合のものも、載せておきます。せっかくいろんなことを教えてくださったのに、もったいないです。ありがとうございました。個人的にはインラインアセンブラを使わない方法に、興味があるのですが、一体どんな勉強をすればよいのか、教えていただけるとありがたいです。

bootpac.c アセンブラありーーーーーーーーーーーーーーーーーー

static __inline__ void io_hlt2(void){
__asm__ (
"HLT"
);

}

static __inline__ void write_mem82(int addr, int data){
__asm__("movb %1,%0": "=m"(*(char *)addr): "r"((char)data): "memory");
}

void HariMain(void)
{
int i;

for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem82(i, i & 0x0f);
}
for (;;) {
io_hlt2();
}

}

エラーログーーーーーーーーーーーーーーーーーーーーーーーーーーーー

C:\TEST\test>make run

C:\TEST\test>..\z_tools\make.exe run
../z_tools/make.exe -r img
make.exe[1]: Entering directory `C:/TEST/test'
../z_tools/make.exe -r haribote.img
make.exe[2]: Entering directory `C:/TEST/test'
../z_tools/cc1.exe -I../z_tools/haribote/ -Os -Wall -quiet -o bootpack.gas bootp
ack.c
../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas
skip:HLT
make.exe[2]: *** [bootpack.nas] Error 1
make.exe[2]: Leaving directory `C:/TEST/test'
make.exe[1]: *** [img] Error 2
make.exe[1]: Leaving directory `C:/TEST/test'
..\z_tools\make.exe: *** [run] Error 2

C:\TEST\test>
ここまでーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー


bootbac.cアセンブラなしーーーーーーーーーーーーーーーーーーーーーーー
static __inline__ void io_hlt2(void){
__asm__ (
"HLT"
);

}

static __inline__ void write_mem82(int addr, int data){
*(char *)addr = (char)data;
}

void HariMain(void)
{
int i;

for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem82(i, i & 0x0f);
}
for (;;) {
io_hlt2();
}

}
エラーログーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

C:\TEST\test>make run

C:\TEST\test>..\z_tools\make.exe run
../z_tools/make.exe -r img
make.exe[1]: Entering directory `C:/TEST/test'
../z_tools/make.exe -r haribote.img
make.exe[2]: Entering directory `C:/TEST/test'
../z_tools/cc1.exe -I../z_tools/haribote/ -Os -Wall -quiet -o bootpack.gas bootp
ack.c
../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas
skip:HLT
make.exe[2]: *** [bootpack.nas] Error 1
make.exe[2]: Leaving directory `C:/TEST/test'
make.exe[1]: *** [img] Error 2
make.exe[1]: Leaving directory `C:/TEST/test'
..\z_tools\make.exe: *** [run] Error 2

C:\TEST\test>
ここまでーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

補足日時:2012/09/04 23:12
    • good
    • 0

"MOV EAX,[ESP+4]\n\t"


"MOV AL,[ESP+8]\n\t"
"MOV [EAX],AL\n\t"
"RET"

だとどうですか?
前回の回答に書いた通り、検索で出てきた例の記述方法をまねているだけなので、うまくいくかはわかりません。

#ちなみに、「的を得ている」ではなく「的を射ている」です。

この回答への補足

回答くださりありがとうございます。

さっそく、試してみたところ。次のようになりました。

C:\TEST\test>make run

C:\TEST\test>..\z_tools\make.exe run
../z_tools/make.exe -r img
make.exe[1]: Entering directory `C:/TEST/test'
../z_tools/make.exe -r haribote.img
make.exe[2]: Entering directory `C:/TEST/test'
../z_tools/cc1.exe -I../z_tools/haribote/ -Os -Wall -quiet -o bootpack.gas bootp
ack.c
../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas
skip:MOV ECX, [ESP+4]
skip:MOV AL,[ESP+8]
skip:MOV [ECX], AL
skip:RET
skip:HLTRET
make.exe[2]: *** [bootpack.nas] Error 1
make.exe[2]: Leaving directory `C:/TEST/test'
make.exe[1]: *** [img] Error 2
make.exe[1]: Leaving directory `C:/TEST/test'
..\z_tools\make.exe: *** [run] Error 2

C:\TEST\test>

です。
先ほどと違うのは、命令を、1つずつ認識している点です。
しかしスキップされています。
このスキップさえ止めればうまくいきそうです。
よろしくおねがいいたします。私もがんばります。

補足日時:2012/09/04 18:31
    • good
    • 0

なんでいろいろと伏せる必要があるのか, さっぱりわからん. そのくらいやましいことをしているという自覚がある? あるいは, あえて情報量を減らすことによって回答者に不要な労力を強いるという策略?



さておき, #2 にもあるように「インラインアセンブラの構文はコンパイラごとに異なる」んだから, 自分の使っているコンパイラに合わせて書き直してください.

なお, インラインアセンブラに手を出すくらいなら「どう書き直せばいいのか」などという質問はしないように.

この回答への補足

回答くださりありがとうございます。伏せているのは、ただ単に私が臆病ものだからです。だったらこんなところに出てくるべきではないと、突っ込まれそうですが、どうしてもわからなかったから、ほかに良い方法がなかったのです。

>なお, インラインアセンブラに手を出すくらいなら「どう書き直せばいいのか」などという質問はしないように.

と、くぎを刺されましたが、最悪の場合、「どう書き直せばいいのか」を質問せねばなりません。そのためにこういうサイトがあるのではないでしょうか。

#5の方には、申し訳ないのですが、私に釘をさすことだけが目的に感じられ、私の質問の答えになっておりません。すでに#2の方の転記だけです。今後、こういった投稿をなさるのならば、私が疲れるので、無視しますので、ご了承ください。

補足日時:2012/09/04 17:52
    • good
    • 0

検索すると、コロンのあとはレジスタの条件等を指定することとなっています。


だとすると、コロンの後に"MOV AL,[ESP+8]"といった命令があると、エラーになるのでは?

asmへの命令は、改行やセミコロンで区切ることになっていると思います。
C言語では、並べて描かれた文字列リテラルは連結されるので、

"MOV EAX,[ESP+4]"
"MOV AL,[ESP+8]"
"MOV [EAX],AL"
"RET"



"MOV EAX,[ESP+4]MOV AL,[ESP+8]MOV [EAX],ALRET"

と同じになるので、うまくいかないのでは? 検索してみると、命令の最後に\n\tを入れているコードを見かけます。
もしくは、コードがそのままコピーしてきたものなら、途中にマルチバイト文字(所謂全角文字)があるせいだとか。

使用するデータを、関数呼び出し時にスタックにある前提で取り出していますが、Cの変数をレジスタに設定する方法がある(前述のコロンを使用するもの)ので、そちらの方法にしたほうがいいように思いますが。

ちなみに、伏字で書いても、伏字部分が容易に推測できる場合には、法的責任を回避することはできません。
仮名で書かれた文章でも、内容から実在の人物が推測できるような場合に、プライバシーの侵害を認めた裁判例があります。
まあ、今回の内容なら、タイトルを堂々と書いても責任を問われるようなことはないでしょうが。

この回答への補足

回答ありがとうございます。私は、性分が臆病ものなので、書籍の名前を伏字で出すのも、ここに質問するのも、怖かったのですが、どうしても解決したくなり、質問するに至りました。

本論に入ります。

おっしゃっておられることが、的を得ていると思います。実際に、コロンなしでコンパイルするとエラーがおっしゃるとおりになりました。プログラムと、エラーメッセージを後で書いておきます。

コロンを使う方法で調べたのですが、グーグルにもあまりなくて、こういう場合どう書けばよいのか、具体例がないので、(あるのかもしれませんが私には見つけることができませんでした。)できれば、コロンありの場合を、どう書けばよかったのかを、ご教授願えないでしょうか。よろしくおねがいいたします。答えを聞くようで、こういうことを願い出るのはためらわれたのですが、もはや、私の頭の中がパニックに近い状態で、一体どの情報が正しいのかわからなくなっているので、何卒よろしくお願いいたします。

bootpack.c----------------------

static __inline__ void io_hlt2(void){
__asm __volatile (
"HLT"
"RET"

);

}
static __inline__ void write_mem82(int addr, int data){
__asm __volatile (
"MOVECX, [ESP+4]"
"MOVAL,[ESP+8]"
"MOV[ECX], AL"
"RET"
);


}
void main(void)
{
int i;

for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem82(i, i & 0x0f);
}
for (;;) {
io_hlt2();
}

}


エラーメッセージーーーーーーーーーーーーーーーーーー

C:\TEST\test>make run

C:\TEST\test>..\z_tools\make.exe run
../z_tools/make.exe -r img
make.exe[1]: Entering directory `C:/TEST/test'
../z_tools/make.exe -r haribote.img
make.exe[2]: Entering directory `C:/TEST/test'
../z_tools/cc1.exe -I../z_tools/haribote/ -Os -Wall -quiet -o bootpack.gas bootp
ack.c
../z_tools/gas2nask.exe -a bootpack.gas bootpack.nas
skip:MOV ECX, [ESP+4]MOV AL,[ESP+8]MOV [ECX], A
LRET
skip:HLTRET
make.exe[2]: *** [bootpack.nas] Error 1
make.exe[2]: Leaving directory `C:/TEST/test'
make.exe[1]: *** [img] Error 2
make.exe[1]: Leaving directory `C:/TEST/test'
..\z_tools\make.exe: *** [run] Error 2

C:\TEST\test>

以上です。
よろしくお願いします。

補足日時:2012/09/04 17:36
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


おすすめ情報