0

私はアセンブリ言語 (TASM 86x) の初心者で、最初のプログラムの割り当てに取り組んでいます。本質的に複雑ではありませんが、この言語に慣れていないので、単純なバブルソートを理解するのに苦労しています。

これまでのところ、魔女の C++ しかプログラムしていません。全体的に最も難しい部分は、構文を把握することです。

タスクは、(ユーザーが入力した) 任意の文字列を取得し、ASCII 値で昇順で再配置することです (たとえば、beda と入力すると、abde が返されます)。

出力については確信が持てませんが、並べ替えが完了した後に表示されるはずです。文字列を入力してコマンドプロンプトを終了するだけなので、混乱しています。時期尚早にコードの終わりを指しているという間違いを犯した場所を追跡できません。

より経験豊富な人が私のコードを見て、正しい方向に向けてくれたり、初心者に何かを説明してくれたりすると、とても感謝しています。

.model small
.stack 100h

.data
request     db 'Enter symbols:', 0Dh, 0Ah, '$'  

    buffer      db 100, ?, 100 dup (0)

.code

start:
    MOV ax, @data                   
    MOV ds, ax                      

; request
    MOV ah, 09h
    MOV dx, offset request
    int 21h

; read string                    ;reading string to buffer
    MOV dx, offset buffer           
    MOV ah, 0Ah                     
    INT 21h                         
    MOV si, offset buffer           


    INC si                        ;going from buffer size to actual length 
                                  ;of the string
    MOV cl, [si]              ;string length - loop counter
    mov ch, [si]                  ;string length - loop counter
    mov bl, [si]                  ;bl will be used to reset inner loop counter 
    DEC cl                        ;correcting the values, since count goes
    dec ch                        ; from 0 to n-1 instead of 1 to n

    inc si                        ;moving to strings first byte


outer:                            ;outer loop

    dec ch                        ;decrease counter each pass
    jz ending                     ;when counter reaches 0 end program
    mov cl, bl                    ; reset inner loop counter value


inner:                            ;inner loop
    mov al,byte ptr[si]           ;assigning byte(sybol) to al
    mov ah, byte ptr[si+1]        ;assigning following byte(symbol) to ah
    cmp al,ah                     ;compare the two
    jle after_switch              ;if the latter's value is higher, no need to switch

スイッチに問題があり、組み立てで正しく機能するかどうかわからない

    mov bh, al           ;main problem-switching values, tried a few different   
    mov al, ah           ;ways of doing it (will show them below), but to no avail
    mov ah, bh           ;using familiar C syntax

    jmp output           ;outputing the value

after_switch:         ;no switch needed

外側のスイッチのどこかに出力へのジャンプがあるはずですが、残りのシーケンスを台無しにすることなくそれを含める方法がわかりません

    inc [si]              ;going to the next byte
    dec cl                ;decreasing inner loop counter
    jnz inner             ;back to the beginning of inner loop until counter reaches 0 (resetting in the outer loop)
    jmp outer             ;if counter reaches zero, get back to outer


output:             ;outputting value from the very first bit 
    mov ah, 2
    mov dl, al          ;which after switch is supposed to be stored in al
    int 21h
    jmp inner           ;returning to inner loop to run next course of comparison

ending:
    MOV ax, 4c00h               
    INT 21h                              
end start

内側のループで以前に試した switch の方法

    mov al,[si+1]
    mov byte ptr[si+1],[si]
    mov byte ptr[si], al

不正なメモリ参照エラーを返しますが、この質問は過去にこのボードで既に回答されていることがわかりました。

同じ方法を試しましたが、dx:di レジスターを利用しました

    mov al, byte ptr[si+1]
    mov dx:[di], [si]
    mov byte ptr[si+1], dx:[di]
    mov byte ptr[si], al

無効なオーバーライド レジスタ エラーを返します。何も見つかりませんでした

4

3 に答える 3

1

論理エラー

mov al, byte ptr[si+1]
mov dx:[di], [si]             <<-- there is no dx:[di] register.
mov byte ptr[si+1], dx:[di]   <<-- memory to memory move not allowed.
mov byte ptr[si], al          <<-- `byte ptr` is superflous, because `al` is already byte sized.

ここでセグメント レジスタを使用できますが、セグメントのみを操作しているdsため、その必要はありません。

したがって、これは有効です:

mov DS:[di],si   <-- ds segment (but DS is already the default)

メモリからメモリへの移動は許可されていないことに注意してください。データは定数から取得する必要があります。

mov [di],1000    <-- direct assignment using a constant

またはレジスタを通過する必要があります

mov ax,[di]
mov [si],ax      <--- memory to memory must be a 2 step process.

覚えて[reg]いるのは記憶です。regはレジスタです
別のエラーは次のとおりです。

inc [si] <<-- error, increases some memory location, not `si` the pointer.
dec cl                ;decreasing inner loop counter
jnz inner             ;back to the beginning of inner loop until counter reaches 0 (resetting in the outer loop)
jmp outer             ;if counter reaches zero, get back to outer

これは次のようになります。

inc si                ;next char in the string
dec cl                ;decreasing inner loop counter
jnz inner             ;back to the beginning of inner loop until counter reaches 0 (resetting in the outer loop)
jmp outer             ;if counter reaches zero, get back to outer

単純化
2 つの値を反転するのに s は必要ありませんmov。次のコードを置き換えます。

mov bh, al           ;main problem-switching values, tried a few different   
mov al, ah           ;ways of doing it (will show them below), but to no avail
mov ah, bh           ;using familiar C syntax

このはるかに単純なバリアントでは、プロセスでレジスタを保存します。

xchg ah, al           ;flip chars around

めくっalah回った後、それらをメモリに書き戻す必要があります。そうしないと、すべての作業が無駄になります。

xchg ah, al           ;flip chars around
mov [si],al           ;save flipped values
mov [si+1],ah         

文字列の末尾を超えて読み取る
このスニペットでは、正しい考えが得られますが[si]、文字列の末尾までずっと実行しているため、1 バイトが多すぎます。

inner:                            ;inner loop
  mov al,byte ptr[si]           ;<<-- both are correct, but [si+1] will read
  mov ah, byte ptr[si+1]        ;<<-- past the end of the string at the last byte  

したがって、このセクションを変更する必要があります。

DEC cl                     ;correcting the values, since count goes
dec ch                     ; from 0 to n-1 instead of 1 to n

これに:

sub bl,2                   ;if bl is used to reset inner loop counter it must be
                           ;adjusted as well.
;sub cl,2                  ;inner loop from 0 to n-2
mov cl,bl                  ;bl=cl, so a mov makes more sense
dec ch                     ;outer loop from 0 to n-1 instead of 1 to n

最後に効率化のヒント
必要がない場合は、決してメモリから読み取らないでください。このコードを変更します。

MOV si, offset buffer           
INC si                        ;going from buffer size to actual length 
                              ;of the string
MOV cl, [si]              ;string length - loop counter
mov ch, [si]                  ;string length - loop counter
mov bl, [si]                  ;bl will be used to reset inner loop counter

これに:

MOV si, offset buffer+1       ;start at length byte of the string    
MOV cl, [si]                  ;string length - loop counter
mov ch, cl                    ;string length - loop counter
mov bl, cl                    ;bl will be used to reset inner loop counter

実行できる速度の最適化は他にもありますが、物事を過度に複雑にしたくはありません。

于 2013-10-16T00:14:57.143 に答える
0

即値をメモリ ロケーションに書き込みたい場合は、アクセスするバイト数を指定する必要があります。1 バイト、ワード、または dword のみです。

mov word [di],1000    <-- direct assignment using a constant (immediate value)

ダーク

于 2014-02-27T11:37:19.393 に答える
0

速度を最適化するために、xchg 命令はすべての x86 で非常に高速な命令ではありません。したがって、代わりに値を切り替えて xchg 命令を置き換えるために 3 番目のレジスタを使用することを好みます。単純な mov 命令は、命令パイプラインのプリフェッチ入力キュー内の他の単純な命令とペアリングしやすく、CPU は単純な命令をより簡単に変換できるためです。マイクロオペレーション(μops)へ。

各パイプラインのプリフェッチ入力キューのステージ数を考慮して、各命令がフェッチ、デコード、ストアなどのように通過する場所で、mov 命令の間に他の命令 (他の命令に依存しない命令) を配置できます。値を切り替えるため、実行中のストールを防ぐため。

失速防止の指示の例:

(注: すべての x86-CPU がパイプラインでまったく同じ数の実行ステージを使用するわけではありませんが、回路図は似ています。)

命令の配置が悪いと、ストールが発生します。

mov eax,value1
add eax,value2 ; reading after writing the same register = results a stall
mov ebx,value3
add ebx,value4 ; stall
mov ecx,value5
add ecx,value6 ; stall
mov edx,value7
add edx,value8 ; stall
mov esi,value9
add esi,value10 ; stall
mov edi,value11
add edi,value12 ; stall
mov ebp,value13
add ebp,value14 ; stall

命令のより良い配置:

mov eax,value1 ; first instruction
mov ebx,value3 ; second instruction
mov ecx,value5 ; third ...
mov edx,value7 ; ...
mov esi,value9
mov edi,value11
mov ebp,value13
; no stall, because the execution progress of the first instruction is fully complete
add eax,value2
add ebx,value4 ; ... of the second instruction is fully complete
add ecx,value6 ; ... of the third instruction is fully complete
add edx,value8 ; ...
add esi,value10
add edi,value12
add ebp,value14

ダーク

于 2014-02-27T11:15:23.470 に答える