-1

emu8086 で asm x86 コードを作成する際に、隣接行列とノード数が与えられたグラフ (cicles なし) のトポロジカル ソートを見つける際に大きな問題があります。私はいくつかのアイデアを試しましたが、何もうまくいきませんでした...だから、これを解決する方法、またはこの問題にアプローチする方法について、(言葉またはコードで)助けてくれる人がいれば、それは素晴らしいことですどうすればいいのかわからないので...データは次のように与えられます。

JMP main
size db 4
graph db 0 ,1 ,1 ,0 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,0
ordering db 0 ,0 ,0 ,0
main :

これを解決するには、DFS アルゴリズムが最適だと思います。しかし、繰り返しますが、私は心からすべてを試しましたが、これまでのところ何もうまくいきませんでした...だから、どんな助けにも感謝します. 前もって感謝します!!!(そして悪い英語でごめんなさい)

編集:これを書きましたが、まったく機能しません:

JMP main
size db 4
graph db 0 ,1 ,1 ,0 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,1 ,0 ,0 ,0 ,0
ordering db 0 ,0 ,0 ,0  
permanente db 0, 0, 0, 0  

main :
MOV CL,1
PUSH CX
LEA BX,graph
PUSH BX
CALL visitar
RET

visitar:
PUSH BP
MOV BP,SP 
MOV BX,[BP+4]
MOV CX,[BP+6]
LEA DI,size
MOV DX,[DI]
MOV SI,0  

for:
CMP SI,DX
JE end_for
CMP [BX+SI],1
JE nodo
JMP next

nodo:
MOV CX,SI
ADD CX,1
PUSH CX
MOV AX,SI
MUL size
LEA BX,graph
ADD BX,AX
PUSH BX
CALL visitar

next:
ADD SI,1
JMP for 


end_for:
LEA DI,permanente
ADD DI,CX 
SUB DI,1
MOV [DI],1

MOV SI,DX
LEA DX,ordering

bajar:
SUB SI,1
CMP [DX+SI],0
JE cambiar 
CMP SI,0
JG bajar 

cambiar:
MOV [DX+SI],CX
CMP SI,0
JE return
JMP revisar

revisar:
LEA AX,permanente
MOV SI,0  
sumar:
CMP [AX+SI],0
JE seguir
ADD SI,1
LEA DI,size
MOV DX,[DI]
CMP SI,DX
JE return
JMP sumar
seguir:
JMP nodo

return:
POP BP
RET 4
4

1 に答える 1

0

ソースを読んで見つけたいくつかのバグ (リストの完全性を保証するものではありません。他にもあるかもしれません):

LEA DI,size
MOV DX,[DI]

サイズはバイトとして宣言されていますが、単語をフェッチするため、実際にはグラフからサイズ + 最初のエッジをフェッチしています。最初のエッジが 1 の場合、dx予想される 4 ではなく 300 になります。

size宣言ごとに単語に拡張するかdw、バイトのみで作業します。

また、アドレスのようにアドレスから直接サイズを取得することもできます。アドレスmov dx,[size]は必要ありませんlea


CMP [BX+SI],1

この構文は好きではありません。比較されるサイズが明確ではありません。emu8086 構文に何があるかわかりません。byteどちらかの側にBYTE 1、またはBYTE PTR [bx+si]TASM/MASM 構文のように追加してみてください。そのうちの1つが機能することを願っています。

これは「スタイル」設定のように見えるかもしれませんが、エッジ定義が 0/1 よりも複雑で単語を使用する場合、バグになります (デフォルトの結果はバイト比較であると思われるため)。


MUL size

詳しくは取扱説明書をご確認ください。他の 1 つのレジスタの内容を破壊すると、for失敗する可能性があります。

また、構文が好きではありませsizeん。アドレス自体ではなく、アドレスのメモリからの値が必要なため、[]サイズを追加すると理解しやすくなり、メモリの内容に関心があることがわかります。

最後に、データサイズが指定されていません。私はそれを完全に見逃していました。したがって、アセンブラがこれをバイトとして扱う場合、破壊されたレジスタの内容に関する最初のコメントは無効ですが、単語指定子が必要な ax*size を実行したかったようです。ところで、すでにサイズが にロードされてdxいるため、mul dxまたはmul dlメモリアクセスを節約できます。


CALL visitar

高レベルのドキュメントが不足していることが、ここであなたを捕まえます。

アセンブリ関数を作成するときは、常に、パラメーターとは何か (呼び出し方法)、結果 (存在する場合) は何か、変更されるレジスターは何かをコメントで文書化します。

多くvisitarのレジスタを変更しますが、forループ中に保持されることを何らかの形で期待しsiています。そうではありません。その後cx、元のノード番号として作業しますが、呼び出しパラメーターの準備中に既に破棄されています。など...再帰呼び出しであるため、重要なすべてのレジスタが破棄されることはほぼ確実です。


orderingバイト配列として定義されている間、単語をに格納し、オフセットはバイト単位です。


MOV CL,1
PUSH CX

chここでは初期化されていません (emu8086 ではおそらくゼロですが、ターゲット プラットフォームによって異なる状態で「開始」が呼び出される可能性があるため、すべてを初期化することをお勧めします。


あなたが持っているものを改善する方法:

グローバル変数とは何か、ローカル変数とは何かという設計作業を行います。次に、実行中にサイズが変わらない、または順序 [next_to_write] ポインターがグローバルなどのように、グローバルを保持しようとする場合があります。

値を保持するために呼び出す前に、重要なレジスタをプッシュ/ポップvisitarします。または、関数の引数にスタックを使用するためにスタック フレームを使用する場合は、ローカル変数をスタックに入れる (sub sp,reserved_spaceおよび でアクセスする[bp-x]) ことを検討してください。そうすれば、メモリアクセスのために動作が遅くなりますが、変更されたレジスタについて心配する必要はありません。

すべての値の正しいサイズを使用していることを確認してください。ノード数 ( size) がバイトかワードかを決定します。これにより、残りの変数がほぼ決定されます。あなたのgraph定義は size*size long ですが、実際には最大 200 ノードを超えることはできないため、byteサイズのみを指定しても問題ありません。しかし、それはそれに固執することを意味します。mov [dx+si],clノード番号を に格納しますordering。また、グラフに対処するときはmul、16+ サイズでも機能することを確認してください。


スタイルノート:

add/sub r16,1inc/dec説明書もあります。

LEA AX,permanente- 元の Intel 構文では、式がメモリへのアクセスに似ている必要があるため、LEA AX,[permanente]Intel が意図したように見えます。ただし、lea実際にはメモリ コンテンツで動作していないため、もともと Intel の決定にはあまり賛成できませんでした。それからまたlea ax,bx+si*2+permanente奇妙に見えます。

MOV SI,0- xor si,si- 私が学んだ最初の「トリック」の 1 つであり、命令がレジスタとメモリで何をするかを正確に定義していることを理解させてくれました。その効果を使用して目的を達成できる場合は、それらのどれでも自由に使用できます。 、その名前がその使用法を直接暗示していなくても。

leaいくつかの異なるアドレス指定モードを試してみてください。常にすべての変数アドレスを使用する必要はありません。16b モードでどのレジスタが許可されているかはわかりません (32b 保護モードではさらに多くの組み合わせが許可されるため、16b では機能しない [eax+ecx*8] などに慣れています) [permanente+si]。正常に動作するなど


ああ、もちろん、デバッガーを入手して、その使い方を学びましょう。デバッガー内のいくつかのステップで、元のコードが予期しない状態になるはずです。

また、3 ~ 10 命令のすべてのグループのように、いくつかの「ハイレベル」コメントをソースに含めるようにしてください。また、レジスタの割り当て、どこでどのような値を期待するかについてのメモ。

于 2016-10-19T05:19:08.137 に答える