このアセンブリコードが無限ループになるのはなぜですか?

itasahobby

簡単な説明

プログラムは、int 0x10を使用してASCIIで指定された文字を使用してピラミッドを出力する必要があります。3行の期待される結果(以下のコードで使用される量)は次のようになります。

A

aa

aaa

コードをコンパイルして実行するには、コードをコンパイルしてnasmから、次のようqemuにエミュレートします。

nasm pyramid.asm
qemu-system-x86_64 -drive file=pyramid,format=raw,index=0,media=disk

ただし、プログラムはすべてのASCII値の印刷でスタックします。また、nasmコード用のデバッガーがあり、行ごとに実行できる場合は、学習にも役立つレジスターの値を確認できます。

コード:

[bits 64]
[org 0x7c00]   

mov sil, CHAR            ; Save the char in the sil register.
add sil, 48              ; Adds 48 to display it as ASCII.
mov ah, 0x0e             ; Value in 'ah' needed to be able to use 'int 0x10'.
mov cl, 0x3              ; Setting the counter of lines remaining.
mov bx, 0x1              ; Setting the amount of characters to print.

pyramid: 
    mov dx,bx            ; Creates a copy bx in dx.
    cmp cl,0             ; If we already printed all the lines we exit the program.
    je exit              ;
    jmp printLine        ; Otherwise we print the next line.

printLine:
    cmp dx,0             ; If all characters from the line were printed goes to next line
    je endPrintLine      ; 
    printChar:
        mov al, sil      ; We move the counter to the 'al' register. 
        int 0x10         ; Interruption that prints the content of the register al.
        mov al,0x20      ; We move the value 0x20 (space) to the 'al' register.
        int 0x10         ; Interruption that prints the content of the register al.
        add dx,-1        ; Decrement by 1 the amount of characters remaining.
        jmp printLine    ; Print the next line.
    endPrintLine:        ;
        mov al,0xA       ; We move the vale 0xA (next line) to the 'al' register.
        int 0x10         ; Interruption that prints the content of the register al.
        add cl,-1        ; Decrement by 1 the amount of lines remaining.
        add bx,1         ; Icrement the amount of chars to print by 1.
        jmp pyramid      ;

exit:
    jmp $

CHAR: db "a",0           ; Character we want to make the pyramid of. 

times 510-($-$$) db 0    ; Fill with 0s.
dw 0xaa55                ; Save in 0x511 '0xaa55' to indicate it's bootable.
ecm

NASMのアセンブルを[bits 64]モードに切り替えて、qemuがコードをロングモードで実行することを期待することはできません呼び出す方法は、Real 8086モードbits 16(NASMのデフォルト)を使用する必要があることを示唆しているようです多くの8ビットまたはサイズに依存しない操作を使用しているため、コードは何らかの方法で実行されますが、期待どおりに実行されない可能性があります。

また、mov al, sil「カウンターを移動する」とコメントしましたalが、それはカウンターではありません。また、イニシャルmov sil, CHARは「CHAR」が指す文字をに入れません。sil実際には、CHARのアドレスをレジスタに入れます(silこれはR86Mのように解釈されます)。またadd sil, 48、意味がありません。48(30h)は、10進数(0から9)を数値からその数値のASCII数字に変換するために追加する正しい値です。これは一般的な「ASCIIとして表示する」変換ではなく、10進数の1桁の数値に対してのみ機能します。

また、qemuの実行が永久にループしてスタックし、さまざまな文字が無限ループで表示されることについても言及していません。

コードの16ビットモードでの逆アセンブルは次のとおりです。

$ ndisasm -b 16 -k 0x3D,$((512 - 0x3D)) pyramid
00000000  40                inc ax
00000001  B63D              mov dh,0x3d
00000003  40                inc ax
00000004  80C630            add dh,0x30
00000007  B40E              mov ah,0xe
00000009  B103              mov cl,0x3
0000000B  66BB01006689      mov ebx,0x89660001
00000011  DA80F900          fiadd dword [bx+si+0xf9]
00000015  7424              jz 0x3b
00000017  EB00              jmp short 0x19
00000019  6683FA00          cmp edx,byte +0x0
0000001D  740F              jz 0x2e
0000001F  40                inc ax
00000020  88F0              mov al,dh
00000022  CD10              int 0x10
00000024  B020              mov al,0x20
00000026  CD10              int 0x10
00000028  6683C2FF          add edx,byte -0x1
0000002C  EBEB              jmp short 0x19
0000002E  B00A              mov al,0xa
00000030  CD10              int 0x10
00000032  80C1FF            add cl,0xff
00000035  6683C301          add ebx,byte +0x1
00000039  EBD4              jmp short 0xf
0000003B  EBFE              jmp short 0x3b
0000003D  skipping 0x1C3 bytes

分解されたマシンコードを段階的に見ていきましょう。

mov sil, CHARビットは以下のようにデコードされinc ax、その後(REXプリフィックスバイト、40H) mov dh, 3Dh

00000000  40                inc ax
00000001  B63D              mov dh,0x3d

次に、別のREXプレフィックスバイトとadd dh, 30h

00000003  40                inc ax
00000004  80C630            add dh,0x30

これでdh6Dh( 'm')になります。

次の2つの命令は、REXプレフィックスバイトを使用しない8ビット演算であるため、意図したとおりに解釈されます。

00000007  B40E              mov ah,0xe
00000009  B103              mov cl,0x3

次にmov bx, 1、O16プレフィックス(32ビットまたは64ビットモードのOSIZE)でアセンブルされたものを取得します。これは、16ビットコードセグメントにあるため、代わりにO32として解釈されます。

0000000B  66BB01006689      mov ebx,0x89660001

O32プレフィックス付きBBhがmov ebx, imm32意図したものではないため、逆アセンブラは間違った命令を続行しますmov bx, imm16

00000011  DA80F900          fiadd dword [bx+si+0xf9]

これは、このコンテキストでは基本的に操作なしの指示です。次に、ジャンプします。

00000015  7424              jz 0x3b
00000017  EB00              jmp short 0x19

私は信じているinc ax(と、最も可能性がない-ゼロ(NZ)の状態にフラグを残してfiadd、あなたはので、それを変更しません)jzここでブランチをしません。

00000019  6683FA00          cmp edx,byte +0x0
0000001D  740F              jz 0x2e

この比較は、全体で行われedxます。符号拡張された8ビットイミディエートを備えた最適化された形式により、目的のO16からO32への唯一の変更は、edxレジスタ全体が比較されることです。ただし、の上位ワードedxが含まれているため、このループは4ギガを超える反復で実行される可能性があります。

0000001F  40                inc ax
00000020  88F0              mov al,dh

この場合も、silレジスタは代わりにREXプレフィックスバイト(incとしてデコードされ、次にdh。のアクセスとしてデコードされますこれが、無限ループが異なる文字を表示する理由ですal。ループカウンターの中央のバイトから初期化しています。

00000022  CD10              int 0x10
00000024  B020              mov al,0x20
00000026  CD10              int 0x10

ここでは驚きはありません。すべて意図したとおりに解釈されます。

00000028  6683C2FF          add edx,byte -0x1
0000002C  EBEB              jmp short 0x19

この追加によりedx、qemuからプログラム渡される初期値に応じて、非常に長いループが作成されます。

0000002E  B00A              mov al,0xa
00000030  CD10              int 0x10
00000032  80C1FF            add cl,0xff
00000035  6683C301          add ebx,byte +0x1
00000039  EBD4              jmp short 0xf

ここではそれほど多くの驚きはありません。ただし、のebx代わりにインクリメントされますbx

0000003B  EBFE              jmp short 0x3b

この停止ループは、希望どおりに解釈されます。

ラベルへのループバックは、pyramidそのコードフラグメントを次のように解釈します。

$ ndisasm -b 16 -s 0xF -k 0x3D,$((512 - 0x3D)) pyramid
[...]
0000000F  6689DA            mov edx,ebx
00000012  80F900            cmp cl,0x0
00000015  7424              jz 0x3b
[...]

したがって、ループカウンタedxをの完全な値に初期化しますebxこれにより、ループが非常に長くなります。cmp cl, 0意図として解釈されます。


これがあなたのプログラムの修正された書き直しです。それは使用されません。silあなたが使用することはできませんので、もうsil16ビットモードでは、それがとにかく必要とされていませんでした。割り込み10hah = 0Ehサービスで使用される可能性があるbxため、内部ループカウンタのリセット値としては使用れませんbxさらに、fullcxを外部ループカウンターとして使用します。これは必須ではありませんがloopdec cl\の代わりに命令を使用できますjnz .loop_outer

さらに、プログラムの2つのエラーを修正しました。

  • 行ごとにピラミッドを構築する最後の文字の後に、末尾の空白が続きます。最初に空白を表示し、次に他の文字を表示するようにプログラムを変更しました。

  • 改行には10(0Ah、改行)文字コードのみを表示しました。正しいのは、割り込み10時間のサービスレベルで13(キャリッジリターン)、次に10(ラインフィード)です。

もう1つの問題は、jmp停止するためにプレーン使用したことですこれは永久にループするため、多くのCPU時間を消費します。私が使用sti\ hlt\jmp停止しながら、ゼロにQEMUプロセスの近くのCPU時間を保つ代わりにシーケンスを、。

ソースは次のとおりです。

        ; cpu 386       ; no 32-bit registers used or needed here!
        cpu 8086
        bits 16
        org 0x7c00

start:
        mov ah, 0Eh     ; value to call "display TTY" int 10h service
        mov cx, 3       ; outer loop counter
        mov di, 1       ; inner loop counter initialisation,
                        ;  incremented by each outer loop
        mov bx, 7       ; bx initialised to 7 for Int10.0E "page" and "colour".

                ; Note: Do not use bp register, as it may be overwritten by
                ;        the Int10.0E call.

.loop_outer:
        mov dx, di      ; reset inner loop counter to di

.loop_inner:
        mov al, ' '
        int 10h         ; display a blank
        mov al, 'a'
        int 10h         ; display the character we want to show
        dec dx
        jnz .loop_inner ; loop inner loop

        mov al, 13
        int 10h
        mov al, 10
        int 10h         ; display a line break

        inc di          ; increment reset value for inner loop counter
        loop .loop_outer; loop outer loop

halt:
        sti
        hlt             ; halt the system (without using much CPU time)
        jmp halt

        times 510-($-$$) db 0
        dw 0AA55h

次のように実行します。

$ nasm p.asm -l p.lst -o p.bin
$ qemu-system-x86_64 -drive file=p.bin,format=raw,index=0,media=disk

qemuに次の出力が表示されます。

[...]

Booting from Hard Disk...
 a
 a a
 a a a
[cursor here]

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

このコードが無限ループになるのはなぜですか?

分類Dev

このCコードが無限ループになるのはなぜですか?

分類Dev

このコードが無限ループに入るのはなぜですか?

分類Dev

このコードが無限ループに陥るのはなぜですか?

分類Dev

このReactコードで無限ループが発生するのはなぜですか?

分類Dev

このソリューションが無限ループに陥らないのはなぜですか?

分類Dev

このjavascriptループが無限に戻るのはなぜですか?

分類Dev

Jasminがこのアセンブリコードを気に入らないのはなぜですか?

分類Dev

このコードスニペットがCとC ++で根本的に異なるアセンブリコードを生成するのはなぜですか?

分類Dev

このコードが無限ループを引き起こさないのはなぜですか?

分類Dev

なぜここに無限ループがあるのですか?(リンクリスト印刷)

分類Dev

UndecidableInstancesを使用するこのコードがコンパイルして、ランタイム無限ループを生成するのはなぜですか?

分類Dev

ここでforループが無限ループになるのはなぜですか?

分類Dev

このMOVSBアセンブリ言語コードが機能するのはなぜですか?

分類Dev

JavaScriptコードに潜在的な無限ループがあるのはなぜですか?

分類Dev

このコードが無限ループを引き起こすのはなぜですか?

分類Dev

このコードが無限ループを引き起こすのはなぜですか

分類Dev

このメソッドが無限ループに入るのはなぜですか?

分類Dev

インラインアセンブリコードがトリプルフォールトを引き起こすのはなぜですか?

分類Dev

これがwhileループで無限ループになるのはなぜですか?

分類Dev

なぜこれが無限ループになるのですか?

分類Dev

なぜこれが無限ループ[SICP]になるのですか?

分類Dev

次のコードが無限再帰になるのはなぜですか?

分類Dev

コードが無限ループを作成しているときに、このJavaが実行するのはなぜですか?

分類Dev

このアセンブリコードがそれ自体を変更しないのはなぜですか

分類Dev

セクションが無限ループにコンパイルされた場合、なぜこれが発生するのでしょうか。

分類Dev

これがwhileループによって無限ループが発生するのはなぜですか?

分類Dev

ajaxヘルパーを使用しているときに、これがアクションの無限ループを作成するのはなぜですか?

分類Dev

ソーシングスクリプトで実行したときに逆アセンブルコマンドの出力がないのはなぜですか

Related 関連記事

  1. 1

    このコードが無限ループになるのはなぜですか?

  2. 2

    このCコードが無限ループになるのはなぜですか?

  3. 3

    このコードが無限ループに入るのはなぜですか?

  4. 4

    このコードが無限ループに陥るのはなぜですか?

  5. 5

    このReactコードで無限ループが発生するのはなぜですか?

  6. 6

    このソリューションが無限ループに陥らないのはなぜですか?

  7. 7

    このjavascriptループが無限に戻るのはなぜですか?

  8. 8

    Jasminがこのアセンブリコードを気に入らないのはなぜですか?

  9. 9

    このコードスニペットがCとC ++で根本的に異なるアセンブリコードを生成するのはなぜですか?

  10. 10

    このコードが無限ループを引き起こさないのはなぜですか?

  11. 11

    なぜここに無限ループがあるのですか?(リンクリスト印刷)

  12. 12

    UndecidableInstancesを使用するこのコードがコンパイルして、ランタイム無限ループを生成するのはなぜですか?

  13. 13

    ここでforループが無限ループになるのはなぜですか?

  14. 14

    このMOVSBアセンブリ言語コードが機能するのはなぜですか?

  15. 15

    JavaScriptコードに潜在的な無限ループがあるのはなぜですか?

  16. 16

    このコードが無限ループを引き起こすのはなぜですか?

  17. 17

    このコードが無限ループを引き起こすのはなぜですか

  18. 18

    このメソッドが無限ループに入るのはなぜですか?

  19. 19

    インラインアセンブリコードがトリプルフォールトを引き起こすのはなぜですか?

  20. 20

    これがwhileループで無限ループになるのはなぜですか?

  21. 21

    なぜこれが無限ループになるのですか?

  22. 22

    なぜこれが無限ループ[SICP]になるのですか?

  23. 23

    次のコードが無限再帰になるのはなぜですか?

  24. 24

    コードが無限ループを作成しているときに、このJavaが実行するのはなぜですか?

  25. 25

    このアセンブリコードがそれ自体を変更しないのはなぜですか

  26. 26

    セクションが無限ループにコンパイルされた場合、なぜこれが発生するのでしょうか。

  27. 27

    これがwhileループによって無限ループが発生するのはなぜですか?

  28. 28

    ajaxヘルパーを使用しているときに、これがアクションの無限ループを作成するのはなぜですか?

  29. 29

    ソーシングスクリプトで実行したときに逆アセンブルコマンドの出力がないのはなぜですか

ホットタグ

アーカイブ