パスワードを忘れた? アカウント作成
271030 journal

KMYの日記: str2num、scan、2つのマクロは最終的にこうなった。 14

日記 by KMY

str2num macro string:REQ
    local   i
    local   res
 
    .data
        i   DD  0
        res DD  0
 
    .code
        ; 文字列へのポインタをiに代入
        mov i, string
 
        ; mulのための初期化
        mov edx, 0
 
        ; 文字の桁数だけ繰り返す
        .while 1
            ; eaxにvarの最初の1文字をコピー
;            invoke  MoveMemory, addr tmp, i, 1
            mov eax, 0
            mov ebx, i
            mov al, byte ptr [ebx]
            .break .if al == 0
            sub eax, 48
 
            ; eaxの値を一旦箱に入れる
            push    eax
 
            ; resに10をかける
            mov eax, res
            mov ebx, 10
            mul ebx
            mov res, eax
 
            ; eaxを取り出してresに加算
            pop eax
            add res, eax
 
            ; ループカウンタを1減らす
            inc i
        .endw
 
        ; 結果を代入
        mov eax, res
endm
 
scan macro
    local   text
    local   strs
    local   rcHandle
 
    .data
        rcHandle DD 0
        strs     DD 0
 
    .data?
        text     DB 48  dup(?)
 
    .code
        invoke GetStdHandle, STD_INPUT_HANDLE
        mov rcHandle, eax
        invoke ReadConsole, rcHandle, addr text, 48, addr strs, NULL
 
        mov     ebx, strs
        mov     byte ptr [text+ebx-2], 0
;        invoke  ZeroMemory, eax, 1
 
        lea     eax, text
endm

講座を書く資格はないと言われたので
どうしようかぼんやり一考中。

考えるまでもないだろと言われればそれまでですが、
とりあえずここを延々とMASMのコードで荒らすという地味な(?)作業は続けます。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • 講座というより、自分の勉強ノートを後悔公開する感じでいいんじゃないですか?

    個人的には、これまでのを見る限り基礎に非常に不安を覚えるので、まずはもうちょっと基礎的なところをやるのをお勧めするわけですが…
    (既にアセンブラの知識があるならリファレンスマニュアル見るだけで1日でプログラムは組めるようになりますが、全くの初めてならとりあえず1冊はチュートリアル本に目を通したほうが良いのではないかと…(通してないように思えるので…))
    • はぅっ
      一晩中、全く同じことを考えていました。

      あくまでアセンブリではなくWindowsのアプリケーション制作に重点を置いているので
      基本的なところをすっとばしていきなりその先の話をしたり(例:.while)
      そういうところはありますが、
      他の人ではない、自分のための講座だと思って書いていくことにします。

      まだまだ浅学な私にいろいろアドバイスしていただき、ありがとうございます!

      --
      By KMY
      親コメント
  • いや、最終版に落ちるの早すぎ。もっと努力せよ。

    str2num macro string:REQ
    K/ecode>
    というこれを
    <ecode>
    str2num macro string:REQ, base:=10
    K/ecode>
    と書き直す。その上で
    <ecode>
                ; resに10をかける
                mov eax, res
                mov ebx, 10

    という部分を

                ; resに 底 をかける
                mov eax, res
                mov ebx, base

    としてみたまえ。これだけで2進数から10進数まで対応のマクロになる。呼び出し引数が1つ増やせるようになって、従来通りなら base は10として扱われるが、そうでなければ

    厳密にはbaseのチェックが甘いがそれは string のチェックが甘いのと、実は MASM で base が 2,3,4,5,6,7,8,9,10 のどれかであることをチェックするのは意外と面倒なので省略。

    .

    マクロにはいくつか役目があるが、このように「パラメータを変化させるだけでプログラムコード的には変化しない」ものを1まとめにするのが役目の1つ。

    もう一つの役目は、「連動するパラメータ」に1つのシンボル名を与えることで、コード中に即値を書かない、というのもある。上記の「10」は仮にマクロのパラメータにしなくても、名前を与えるべきだ。

                sub eax, 48

    の48も同様だ。48は唐突すぎてなんだか判らないだろう?! '0' と書けば判りやすくなるが、それでもASCII文字コード限定だ。こういうのも名前をつけることで「EBCDIC文字コード」対応にできる。たとえば

                charcode_zero = 48
                :
                sub eax, charcode_zero

    のようにすれば、EBCDIC対応の場合も charcode_zero の定義を 'f0h' にするだけで済む。charcode_zeroが良い名前かどうかはともかくとして。
    # 定義を引数にするか、内部で定数として持っているか、の違いでしか無いのは判るだろうか?

    --
    fjの教祖様
    • >str2num macro string:REQ, base:=10

      str2num macro string:REQ, base:=<10>
      じゃないとダメかも。
      もしかしたら、バージョンによってエラーは出ないのかもしれませんが、とりあえず<>で囲んでおいたほうが良いかと。


      >実は MASM で base が 2,3,4,5,6,7,8,9,10 のどれかであることをチェックするのは意外と面倒なので省略。

      あまり気にしたこと無いけど、10 で進数取れませんかね?

      .IF eax >= 10

      と書くと、eax が現在の .RADIX で一桁で表現できる数値の範囲外か判定できるかと。
      (ただ、ASSUME eax:SDWORD されていると (DWORD PTR eax) >= 10 と書かないとダメですが)
      親コメント
      • by KMY (41075) on 2010年11月07日 16時15分 (#1854540) 日記

        恥ずかしながらEBCDICという文字コード自体知りませんでした。
        なるほど‥‥アルファベットの文字コードにまで種類があったんですね。勉強になります。

        2~9進数のことも考えて、というのはちょっと思いつきませんでした。
        今後、「最終的に」とかいう表現は慎みます。

        48引く代わりのaaaというニモニックを見つけたけど‥‥これはASCII限定な上にアルファベットにまで対応してしまっているから駄目か。

        --
        By KMY
        親コメント
      • .RADIX は値を設定したり開放したりが面倒ですよね。
        いままでn進数だったのを一時的に16進数でチェックして、再びn進数に戻す…なんてことができない。

        そのニーズが出た段階で、私はcygwin の m4 + as + gcc -mingw に逃げてしまいます(^^;)。
        # MASM より古いけれど、m4 の方がマクロとして高機能。
        # m4 があるなら MASMよりも「マクロ機能のない」asの方が快適。

        --
        fjの教祖様
        親コメント
        • いや、っていうか、なぜ、.RADIX で変更したものを戻したりとかする必要があるのか良く分からないのですが…
          そもそも、.RADIX を変更するシーンってどういうものですか?
          親コメント
          • うーわー、面倒な質問を初心者の日記で…。

            以下の説明に出てくる文法の多くは MASM には存在しない文法です。
            という注意書きを最初に書かなくちゃいけないじゃないか。

            というわけで、MASMにはない文法のお話。MASMでは実装できません。

            .

            たとえば。Express_RADIX()という命令があるとしましょう。Express_RADIX(val, base)のように使い、val は「数値表現」、base は基数を『現在の基数で』表したものとします。
            # つまり val はマクロにとっては文字列であって、『現在の基数』の影響を受けない。

            compare_2_order   macro   val:REQ, base:=<10>
                 .IF val < Express_RADIX( 100, base )
                   xxxx
                 .ENDIF
            endm

            と書けます。この場合「100」の部分はまさに「100」でいい。10進数の100ではなく、「まさに100base」との比較をしていると表現できる。

            .

            もちろん、Express_RADIX なんかなくても似たようなことは出来て、

            compare_2_order   macro   val:REQ  base:=<10>
                 .IF val < ( base * base )
                 xxxx
                 .ENDIF
            endm

            と書けばよい。普通のプログラマなら、「これで良いじゃん、十分分かるよ」と言うでしょう。

            でも、そもそも「マクロ」というのは、as のような「.obj (あー、本当は .o なんだけどまぁ、いいよね)」の内容と1対1に対応するコマンドしか書けないアセンブラだけじゃ、書いていて辛いから発生したわけで。「なんとなく意味が通じる」じゃなく、「言いたいことが言いたい様に表現できる」のが本来あるべきだと思うんですよ。しかも、マクロのように「入れ子」になった表現において、入れ子になったもの同士が互いに悪影響を与えないように適切なカプセル化をするべきだと。

            MASMは「マクロ」アセンブラとして不適切だ、という議論が大昔あったのですが、その理由のひとつがこの
            「マクロ同士がカプセル化できない命令体系」
            にあったと記憶しています。で、その最大の問題点は、変更した値を「前の値」に戻すための push/pop 機能がほとんどない、という点だったかと。

            --
            fjの教祖様
            親コメント
            • いや、そもそもアセンブラ上のリテラルとして登場する数値の進数は、BやらOやらTやらHやらを付けて混合表現できるわけで…

              それに .RADIX で変更しなくても、特定数字列を任意真数の数値に変換するマクロを書けば良いし、そんなのとても簡単ですよ?
              それをわざわざ不適合な記法の例で MASM はだめだというのなら、他の言語だって同じですよね?
              親コメント
            • あとどれがMASMで実装できない文法なのか、私には良く分からなかったのですが、どこでしょうか?

              Express_RADIX マクロ関数も普通に MASM で書けますしね…

              因みにマクロの中に .RADIX を使っても書けますよ。

              ただ、現在進数の数値を10進文字列に変換するマクロ関数はもしかしたら別に書かなければいけないかもしません。

              マクロの中で .RADIX を変更して、元に戻す方法ですが、

              Express_RADIX MACRO val:REQ, base:REQ
                        LOCAL old_radix
                        old_radix = 10
                        .RADIX 10 ; 例えば10進に変更する
                        ; ... 何かの記述
                        .RADIX old_radix
              ENDM

              これで出来ます。
              まあ、私はあまりやりたくない方法ですけど。

              親コメント
              • 連投スイマセン、補足です。

                MASM の .RADIX は10進で値を与えなければならないのですが、例えば

                .RADIX 16
                ; ... 何かの記述
                .RADIX old_radix

                でもちゃんと動作します。(これは確認済み)

                old_radix が数値定数だからです。(テキストマクロでは多分無理だと思います)

                親コメント
              • 度々連投すいません。
                投稿した後に、ふと思い出して調べましたら、PUSHCONTEXT/POPCONTEXT で RADIX も処理できるみたいです。
                (普段、ASSUMES しか使わないのでぼけてました)

                と言うわけで、(#1854912) [srad.jp]のExpress_RADIXマクロ関数は、

                Express_RADIX MACRO val:REQ, base:REQ
                        LOCAL base_num, result_num
                        PUSHCONTEXT RADIX
                        base_num = base
                        .RADIX base_num
                        result_num = val
                        POPCONTEXT RADIX
                        EXITM %result_num
                ENDM

                で、val 数値文字列を、base(現在の RADIX 表現)基底で評価した値を、現在の .RADIX での数値表現した文字列に変換できます。
                記述も

                compare_2_order macro val:REQ, base:=<10>
                          .IF val < Express_RADIX( 100, base )
                              xxxx
                          .ENDIF
                endm

                とまったく同じに記述できて、これを使用したときの挙動も okky 氏の解説通りになると思います。
                マクロの入れ子の問題もありません。

                親コメント
              • このマクロが動かない例:
                    おれ、このマクロを呼ぶ前は7進数で数字を表現してたんだ。
                 このマクロを呼んだら、それ以降が10進数になっちゃったよ。

                # Express_RADIX を呼ぶ前の RADIX が「十」であるなどという保証はどこにも無い。

                --
                fjの教祖様
                親コメント
              • いえ、.RADIX が10でなくてもちゃんと期待通りに挙動しますよ?

                >このマクロを呼んだら、それ以降が10進数になっちゃったよ。

                だから、元に戻りますって。

                old_radix = 10

                では old_radix を数値定数として、マクロが展開された時点の .RADIX での「10」(つまり7進数なら7)が old_radix に代入されます。(マクロの中身は展開されるまでテキストです)
                そしてマクロが終わる前の

                .RADIX old_radix

                では、正しく以前の .RADIX に戻ります。
                これは実際に別さまざまな .RADIX に変更して試しているので間違いありません。
                親コメント
typodupeerror

海軍に入るくらいなら海賊になった方がいい -- Steven Paul Jobs

読み込み中...