* naskの使い方について [#x4df266e] -(2004.04.06 by [[K]]) -最近naskを使ってみようかなという人が出てきてくれたので、その人たちのために。 -主な特徴や最新版入手方法などは、[[GO]]などを参照してください。naskはGOの一部です。 * DOSの.COMファイルの作成例 [#c990cc25] ; prompt>nask hello.nas hello.com hello.lst [BITS 16] [OPTIMIZE 1] [OPTION 1] [INSTRSET "8086"] [FORMAT "BIN"] ORG 0x100 MOV DX,msg MOV AH,0x09 INT 0x21 MOV AX,0x4c00 INT 0x21 msg DB "hello, world", 0x0d, 0x0a, "$" -[BITS 16]というのは、16ビットモード用のバイナリを出せという命令です。 -[OPTIMIZE 1]というのは自動最適化をやれという意味です。 -[OPTION 1]は/や%や>>の扱いで数値を符号付きとして扱わせます。//や%%が符号なしになります。NASMと逆ですね。[OPTION 0]だとNASMと同じになります。 -[INSTRSET "8086"]は8086で規定されているレジスタ名やニーモニックだけを有効にします。この場合たとえばcr3とかecxとかはラベル名として利用できます。8086時代に作ったソースでそういうラベルを使っていた場合、わざわざ修正しないですみます。またこの命令のおかげで、将来のプロセッサで命令やレジスタが増えても安心というわけです。 -[FORMAT "BIN"]はバイナリモード出力を意味します。 -naskではcsegやassumeなどの、初心者にとってうさんくさい(笑)命令はありません。最後にendを置く必要もありません(endはあってもいいですがね)。 * 32bitのWCOFFファイルの作成例 [#t71596e0] ; rand - K&Rからほぼ抜粋したものを単にASM化した [FORMAT "WCOFF"] [INSTRSET "i486p"] [OPTIMIZE 1] [OPTION 1] [BITS 32] [FILE 'rand.nas'] GLOBAL _rand_seed GLOBAL _rand [SECTION .data] ALIGNB 4 _rand_seed dd 1 [SECTION .text] ; int rand(void) _rand: push edx mov eax,dword ds:[_rand_seed] mov edx,1103515245 mul edx add eax,12345 mov dword ds:[_rand_seed],eax shr eax,16 pop edx and eax,07fffh ret * 長ったらしいドキュメント [#sea7f274] -文中ではNASKと書いてありますが、[[K]]は今では小文字でnaskとかかれるほうがなんとなく好きです。 1.NASKについて NASKはNASMからマクロ機能を省き、その代わりに徹底したコードの自動最適化機能を 盛り込んだアセンブラです。アセンブラ自体が結構小さいという特徴もあります。詳し い使い方については、NASMのドキュメントを参考にしてください。サンプルソースもあ るのでNASMと違う部分はだいたい分かっていただけると思いますが、不充分でしたらOS ASKのbase.nasなどをご覧ください。 旧バージョンではバイナリモードしかできませんでしたが、今はWCOFF(win32-COFF)も 使えます。 使い方は単純で、 prompt>nask sorcefile outputfile [listfile] です。listfileを省略するとlistfileを生成しません。 2.NASMとの違いの要約 (短所) プリプロセッサ命令(マクロ)は使えない。 その他にもいくつかの制限がある(3.を参照)。 (長所) 処理系のサイズが小さい(27.0KB)。 最適化の強さがNASMの比ではない。 各セクションのアラインをソース中のALIGN文から自動設定。 文法規制が緩い(ラベルの差をとることなくいきなり&などをしてもよい・・・バイナ リモード時)。 (長所とも短所ともいえる特徴) BITSは[]でくくらなければならない。 その他に、[FORMAT], [OPTIMIZE], [OPTION], [INSTRSET], などの命令があり、ソース の最初に記述しなければいけない。 3.NASK ver.0.0での制限事項(将来改善する仕様?) 単純明快です。 O16, O32, A16, A32, DQ, DT, RESQ, REST, INCBIN, [ABSOLUTE] が使えません。プリプロセッサ命令も使えません(%で始まる命令群のこと)。それ以外の 486DX命令は全て使えます。 プリプロセッサ命令が使いたい場合、NASMの-eオプションなどを使ってプリプロセッ サ処理を終えたソースを生成して、それをNASKにまわすという方法があります。もちろ ん、プリプロセッサが使えないNASKに見切りをつけてNASMを使うというのも有効な手段 ですし、公開されているソースをベースにNASKを改造してもいいでしょう。 immの式のほとんどでラベルを含む式が使えます。駄目なのは、SHL系のシフト量の部 分と、INT命令の番号の部分とRET系のimm16の部分のみです。直せるんですが、時間が なくて直していません。すみません。この駄目な部分に関しても、ラベルを含まない定 数式なら正しく解釈します。 DDでfloatの定数を置くことはまだできません。 TIMESに続く命令は、かならず1バイトにしてください。手抜きです。すみません。 NASMに標準的に備わっているALIGNマクロは使えませんが、制限機能付きのALIGNとAL IGNB命令をサポートしています。これはパラメータを1つしか受け付けません。つまり、 NOPでパディングするか、0x00でパディングするかしか選べないわけです。 これが気に入らなければ以下の表記などで代用してください。 TIMES ( 4 - ($ % 4)) % 4 NOP ; 4バイトアライン TIMES (16 - ($ % 16)) % 16 NOP ; 16バイトアライン 4.特筆すべき(?)仕様 今のところバイナリサイズは27.0KBです。 NASKのラベルは、いつどこで定義しても問題なく参照できます。これはNASMと異なり 単純な2パスではないからです。しかし例外があり、EXTERNラベルは、最初の参照よりも 先に宣言しなければいけません。 FORMATは出力ファイルのフォーマット指定です。NASKでは、コマンドラインではなく ソースで指定します。"BIN"と"WCOFF"があります。デフォルトは"BIN"です。ソースの最 初の方で一回だけ記述します。 NASKでは、BITSを[]でくくらなければなりません。 また、INSTRSETなどの追加命令があるので、NASMとの互換ソースを書く際に問題にな るかもしれません。そのために、「;%NASK」という記述をすることができます。これはN ASMでは当然注釈になりますが、NASKではスペース扱いになります。NASK専用ソースとい うことでしたら、この記述はいりません。 INSTRSETはCPU名を指定します。今のところ以下のCPU名が指定できます。 "8086", "80186", "80286", "80286p", "i386", "i386p", "i486", "i486p" 8086モードでは、FSやEBXなどが予約語になりません。デフォルトは8086です。末尾 にpが付くのはプロテクトモード用の命令群を使えるようにする意味です。なお8086を 選択しているからといって、8086に実行できないコードを出力する可能性がないわけで はありません。単に予約語をラベル定義用に開放しているだけです。ですから、Jccな どでNEARにされてしまうこともありえます。不安な場合は、SHORTを明示しましょう。 OPTIMIZEは最適化のON/OFFです。0だとNASM並みです。1だとNASKの真価を味わえます( 笑)。デフォルトは0です。 OPTIONは/や%、>>の扱いです。0だとNASMのように符号無しになります。1だと符号有 りになります。デフォルトは0です。なお、[OPTION 1]では、//や%%が符号無しになりま す。右シフトについては、OPTIONに関わらず、&>が符号無しシフト、|>が符号有りシフ トです。 NASKでは..$というラベルが使えます。これは次の行のアドレスを返します。数クロ ックのwaitを入れるための懐かしいテクニックとして「JMP SHORT $+2」というのがあ りましたが、これを「JMP ..$」とも書けるわけです。 NASKのBINモードでは、ラベルの差をとらなくても全ての演算が可能です。(LABEL1+L ABEL2)/2とか、LABEL1*5とか、もう好きなようにやってください。$$は恒等的に直前に ORGした値を返します(該当するORG文がない場合は暗黙のORG 0にしたがって0が返ります )。WCOFFモードでの$$の挙動はNASMと同じです。 BINでもWCOFFでも[SECTION]文が使えます。指定できる属性はalignのみです。alignの 指定は最初にそのセクションを宣言したときだけです。BINの場合、セクションは宣言順 にバイナリ化され、アライン指定にしたがってアラインされています。なおalign属性を 全く指定しない場合、NASKはそのセクションに含まれるALIGN、ALIGNB文を探し出して、 そこからこのセクションに付与すべきアラインを自動的に決定します。 WCOFFで利用できるセクションは、.text、.data、.bssのみです。他のセクションを宣 言してはいけません。BINではこれとは無関係に任意の名前をつけられます。 5.メモリアドレッシング (1)セグメントオーバーライドプリフィックス セグメントオーバーライドは、以下の3つの場所にいずれかに書けます。お好きなと ころに書いてください。 ・[]の中(NASM準拠) ・[]の前(MASM準拠) ・オペコードの前("cs movsb"や、"es mul [edi]"のように) なお、OPTIMIZEがONになっていれば、省略可能なDSやSSの指定はあっさりと消し去 られます。これで僕のようにいつでもセグメントを書きたがるプログラマも、気にし ないで書けます(笑)。LEA命令では、どんなセグメントオーバーライドがついていても あっさりとキャンセルされます。 (2)実効アドレス式 NASMのように、かなり複雑な記述を許します。"IDIV BYTE [EBX*3]"なんていうのが OKなのはもちろんですが、"LEA EAX,[(EAX+ECX-5)*8/2-3*EAX]"というのもOKです。ス ケールに割り算を認めるのは、NASMを超えています(もちろん、[EAX*3/2]というのは できない)。また、シフトもできます([EAX<<1])。なお、レジスタに掛け算するときは 掛ける数がラベルやレジスタを含まない式である必要があります。 (3)ベースとインデックス [EAX+ECX+8]みたいな記述では、NASKはどちらをベースにすればいいのかを自分の裁 量で決めて良いと判断します。しかし[EAX*1+ECX+8]や[EAX+ECX*1+8]のようにどちら かに掛け算がかかっていたら、勝手な裁量をせずにそちらをインデックスにまわしま す。掛け算は、1*ECXのようなものでもかまいません。両方に掛け算してあれば、NASK の裁量でやります。・・・というのがnask00aでの仕様でしたが、nask00b以降では、 OPTIMIZEがONのときは掛け算があっても裁量を発揮します。これを阻止するにはNOSPL ITを指定してください。NOSPLITが指定されていれば掛け算を優先します。指定されて いなければ、disp8の省略やプリフィックス省略をねらいます。 [EBP+EDX]のようなものでは、SSやDSが指定されていればプリフィックスを省略でき る方法を求めてどちらをベースにするか選びますし、そうでなければEDXをベースにし て、disp8の省略を狙います。 (4)アドレスモード 実効アドレス式中に32bitレジスタが使われていれば32bitアドレッシング、16bitレ ジスタが使われていれば16bitアドレッシングになります。これらとBITSの内容を比較 して必要に応じてアドレスサイズオーバーライドプリフィックスが出力されます。 もし実効アドレス式がdispのみの場合は、dispの値によらず、アドレスサイズオー バーライドしないという前提でアセンブルされます。もしこれを覆したい場合は、[] 内に、WORDもしくはDWORDと書いて下さい。 * こめんと欄 [#e43c077d] -ふと手元にあったコードアセンブルしてみたんですけど、PWORD PTRとか使えないのかな?それじゃOSASKは?と思って見たらFAR DWORD になってる、ふむ。こっちの方が一般的なのだろうか? -- SIZE(10){2004-05-27 (木) 23:15:18} -はいPWORDは使えません。naskはNASMの文法を大いに参考にしました。たとえばCALL DWORD [DS:ESI]などとした場合、MASMではuse16/32によってfarだったりnearになったりします。一方NASMではBITSにかかわりなく、これはnearになります(BITS16の時は0x66がつけられる)。どっちがいいのかは好みがあると思います。どっちが一般的かというと、個人的にはPWORD(もしくはFWORD)のほうが一般的なような気はします。 -- [[K]] SIZE(10){2004-05-28 (金) 13:17:27} -movsd命令にアドレスサイズプリフィクスを付ける方法はありますか? -- [[名無しさん]] SIZE(10){2008-01-20 (日) 16:50:03} -db 67h はいかがでしょうか。 -- [[名無しさん]] SIZE(10){2008-01-20 (日) 20:12:35} -回答ありがとうございます。…やはりそれですか。手書きで0x67を入れればよいというのはネットで分かったのですが、もっときれいな書き方があるかと思ってソースを読んで挫折しているところです時間もないので、それを使おうと思います。ありがとうございました。 -- [[名無しさん]] SIZE(10){2008-01-20 (日) 22:31:17} -naskを単体でダウンロードさせてはいただけないでしょうか? -- ''ymp'' SIZE(10){2009-02-06 (金) 11:56:48} -それならこちらはいかがでしょうか? http://dot.osask.jp/downloads/?page=singlefile&cid=4&lid=8 -- [[K]] SIZE(10){2009-02-06 (金) 13:03:59} -32bitモードでINTは使えるのでしょうか?まだアセンブラは初心者なんですよ。 -- ''Triangle Ld.'' SIZE(10){2009-05-12 (火) 18:28:43} -その質問は、2つの意味に取れます。「naskにおいて[BITS 32]でINT命令は書けるのか→もちろんほかのx86アセンブラ同様、書けます」「[BITS 32]でINT命令を使ってBIOSを利用できるのか→そのような個別のプログラミングの話題はこのページ向きではありません。このサイト内で適当なところは[[impressions]]だと思います」 -- [[K]] SIZE(10){2009-05-13 (水) 11:11:56} #comment