2017-11-06 7 views
1

配列からいくつかの整数をソートするので、配列を出力するためにいくつかの宿題に取り組んでいました。私はコードがうまく動作しているが、私のコードでALの代わりにEAXを使ってみることに決め、エラーに遭遇した。私はそれがなぜあるのか理解できません。ここでEAXを使うことは可能ですか?ASSEMBLY - 32ビットレジスタと16ビットの配列を出力する

; This program sorts an array of signed integers, using 
    ; the Bubble sort algorithm. It invokes a procedure to 
    ; print the elements of the array before, the bubble sort, 
    ; once during each iteration of the loop, and once at the end. 

    INCLUDE Irvine32.inc 
    .data 
    myArray BYTE  5, 1, 4, 2, 8 
    ;myArray DWORD  5, 1, 4, 2, 8 
    currentArray BYTE 'This is the value of array: ' ,0 
    startArray BYTE 'Starting array. ' ,0 
    finalArray BYTE 'Final array. ' ,0 
    space BYTE ' ',0            ; BYTE 

    .code 
    main PROC 

     MOV EAX,0          ; clearing registers, moving 0 into each, and initialize 
     MOV EBX,0          ; clearing registers, moving 0 into each, and initialize 
     MOV ECX,0          ; clearing registers, moving 0 into each, and initialize 
     MOV EDX,0          ; clearing registers, moving 0 into each, and initialize 
     PUSH EDX      ; preserves the original edx register value for future writeString call 
     MOV EDX, OFFSET startArray ; load EDX with address of variable 
     CALL writeString    ; print string 
     POP EDX       ; return edx to previous stack 
     MOV ECX, lengthOf myArray   ; load ECX with # of elements of array 
     DEC ECX        ; decrement count by 1 


    L1: 
     PUSH ECX        ; save outer loop count 
     MOV ESI, OFFSET myArray   ; point to first value 

    L2: 
     MOV AL,[ESI]    ; get array value 
     CMP [ESI+1], AL    ; compare a pair of values 
     JGE L3      ; if [esi] <= [edi], don't exch 
     XCHG AL, [ESI+1]    ; exchange the pair 
     MOV [ESI], AL 
     CALL printArray    ; call printArray function 
     CALL crlf 
    L3: 
     INC ESI      ; increment esi to the next value 
     LOOP L2     ; inner loop 
     POP ECX     ; retrieve outer loop count 
     LOOP L1     ; else repeat outer loop 
     PUSH EDX      ; preserves the original edx register value for future writeString call 
     MOV EDX, OFFSET finalArray ; load EDX with address of variable 
     CALL writeString    ; print string 
     POP EDX       ; return edx to previous stack 
     CALL printArray 

    L4 : ret 



    exit 
    main ENDP 


    printArray PROC uses ESI ECX 

    ;myArray loop 
     MOV ESI, OFFSET myArray        ; address of myArray 
     MOV ECX, LENGTHOF myArray        ; loop counter (5 values within array)   
     PUSH EDX      ; preserves the original edx register value for future writeString call 
     MOV EDX, OFFSET currentArray ; load EDX with address of variable 
     CALL writeString    ; print string 
     POP EDX       ; return edx to previous stack 

    L5 : 
     MOV AL, [ESI]            ; add an integer into eax from array 
     CALL writeInt 
     PUSH EDX      ; preserves the original edx register value for future writeString call 
     MOV EDX, OFFSET space 
     CALL writeString 
     POP EDX       ; restores the original edx register value 
     ADD ESI, TYPE myArray         ; point to next integer  
     LOOP L5            ; repeat until ECX = 0 
     CALL crlf 

    RET 

     printArray ENDP 

     END main 
     END printArray 

    ; output: 
    ;Starting array. This is the value of array: +1 +5 +4 +2 +8 

    ;This is the value of array: +1 +4 +5 +2 +8 

    ;This is the value of array: +1 +4 +2 +5 +8 

    ;This is the value of array: +1 +2 +4 +5 +8 

    ;Final array. This is the value of array: +1 +2 +4 +5 +8 

出力から分かるように、出力は最小から最大まで並べ替えられます。私はALをEAXに移すことができるかどうか確認しようとしていましたが、それは私にエラーの束を与えました。私は32ビットレジスタを使用して同じ出力を得ることができるので、これを回避する方法はありますか?

+2

エラーとともに実際に試したコードを表示します。あなたがしようとしていることが有効であるかどうかを確認するために、命令セットリファレンスをクロスチェックしてください。 PS:dwordsのサイズは4バイトであり、TYPE演算子を使用する代わりに、サイズを '1'としてハードコードした場所があることに注意してください。 – Jester

答えて

1

EAXを使用することは間違いありませんが、実際には既にあります。 「ALをEAXに移すことができるかどうか確認しようとしていたが、それは私にエラーの束を与えた。それが何を意味するか考えてみてください。 EAXは拡張AXレジスタ、ALはAXの下位パーティションです。この図を見てください:image of EAX register 。ご覧のように、おそらくMOVZX命令を使ってALをEAXに移動させると、単にALの値をEAXに入れ、右から左にゼロを埋めます。 ALをALに移動し、EAXの残りの部分を0に設定すると、実際にはすべてをEAXに移動し、同じプログラムを実行することができます。同じ部分のメモリを使用しているため違いはありません。

また、なぜEAXを押してポップしているのですか?ランタイムスタックからプッシュ/ポップする唯一の理由は、後でそれらを回復することですが、決してそれを行うことはありません。

+0

これは素晴らしい図ではありません。 「AX」ラベルはラベル付け中の低16の外側にあり、ALとAHはラベル付け中のレジスタの内側にあります。それは、AXがEAXの高い半分であると考える人を混乱させる可能性があります。 ASCIIアートダイアグラムについては、https://stackoverflow.com/questions/37243801/x86-calculating-ax-given-ah-and-al/37275984#37275984を参照してください。そして、[YASMマニュアルには素晴らしい図があります](https://www.tortall.net/projects/yasm/manual/html/manual.html#x86-registers)。それらはhttps://stackoverflow.com/tags/x86/infoにリンクされているので、すぐにそれらを再度見つけることができます。 –

+0

そしてBTW、いいえ、「movzx」であっても、OPはすべての命令でALを使うことができません。 'CMP [ESI + 1]、AL'は、dword比較の代わりにバイト比較を行うために' AL'を使う必要があります。 * 'AL'を書いている全ての命令は、' xchg'を除いて 'movzx'を使ってEAXを書くことができます(これは3'em'命令より効率的です) –

+0

ありがとうございます。それは実際に今働いています。 – Alex

1

まだ8ビットストアを実行する場合は、には、8ビットレジスタを使用する場合はが必要です。 (ALは8ビットのレジスタですが、IDKではタイトルに16と表記します)。

x86では、ロードが広がります(movzxmovsx)が、レジスタのオペランドの整数ストアは常にメモリオペランドと同じ幅のレジスタを取ります。 EAXの下位バイトを格納する方法は、mov [esi], alである。 printArray

、あなたはmovzx eax, byte ptr [esi]にEAXにゼロ拡張使用する必要があります。 (uint8_tの代わりに数字をint8_tとして扱う場合は、movsxを符号拡張する必要があります。これにより、EAXの上位24ビットをゼロにする必要がなくなります。


ところで、あなたのコードには多くの不必要な指示があります。例えば

MOV EAX,0          ; clearing registers, moving 0 into each, and initialize 

全く無意味です。最初の使用が書き込み専用の場合は、レジスタを初めて使用する前にレジスタを「初期化」または「宣言」する必要はありません。あなたはEDXでやっていることは面白いです:あなたが実際に古い値が欲しい場合

MOV EDX,0          ; clearing registers, moving 0 into each, and initialize 

    PUSH EDX      ; preserves the original edx register value for future writeString call 
    MOV EDX, OFFSET startArray ; load EDX with address of variable 
    CALL writeString    ; print string 
    POP EDX       ; return edx to previous stack 

「発信者-保存」レジスタにのみ保存する必要があります。私は、用語「通話保存」と「通話録音」を好む。 writeStringが入力レジスタを破壊すると、関数が復帰した後、EDXは未知の値を保持しますが、それは問題ありません。あなたはとにかく価値を必要としませんでした。 (実際に私はIrvine32がほとんどのEAXを破壊する機能だと思う)

この場合、前の命令はレジスタ(inefficiently)をゼロにしただけだった。そのブロック全体は次のようになります。

MOV EDX, OFFSET startArray ; load EDX with address of variable 
    CALL writeString   ; print string 
    xor edx,edx    ; edx = 0 

あなたがゼロにするためにそれを必要としないので、実際には、あまりにも-zeroing xorを省略すべきです。あなたはループや何かのカウンターとしてそれを使用していない、他のすべての用途は書き込み専用です。


はまた、リードモディファイライト(個別のmovロードする命令とストアよりも、それはずっと遅くなって)アトミックを行いますので、メモリとXCHGは、暗黙のlock接頭辞を持っていることに注意してください。

movzx eax, word ptr [esi]を使用してバイトのペアをロードし、ブランチを使用してrol ax, 8をスワップするかどうかを決定できます。しかし、バイトストアの転送からワードロードまでのストア転送のストールはあまり良くありません。

とにかく、これはタイトル質問から話題になりつつあり、これはcodereview.SEではありません。

+0

私はまだこれを学んでおり、私はそれをかなり新しくしています。これらの間違いや冗長性を指摘していただきありがとうございます。私はそれをより良くするために働くでしょう。 – Alex

関連する問題