MIPSで複数行の文字列の行順を逆にするにはどうすればよいですか?

ABeeCDee

次の形式の複数行のメッセージがあります。

.data
    msg:
        .ascii "aYZ B"
        .byte 10
        .ascii "234"
        .byte 10
        .ascii "b cd A"
        .byte 10

そして、次のように、行が印刷される順序を逆にする必要があります。

aYZ B ---------------- b cd A

234 ----になる--- 234

b cd A ---------------- aYZ B

これまでの私の一般的な考え方は、最初の文字のアドレスをスタックにプッシュしてから、メッセージ(msgのベースアドレス+オフセットカウンター)を繰り返し処理し、 '\ n'文字(.byte)の直後にすべての文字のアドレスをプッシュすることです。 10)スタックに( '\ n'char + 1のインデックス)。

次に、各行の最初の文字を逆の順序でスタックからポップできるようになります。

私が苦労しているのは、元のメッセージをループしながら変更する方法です。新しいメッセージを逆の順序で作成する必要がありますか?もしそうなら、どのように?これには文字列連結を使用すると思いますか?

最後に、そのメッセージを印刷するにはどうすればよいですか?(syscall 4を使用できますが、メッセージ全体を1つのラベルに保存する必要があります)。

編集:

だから私はなんとか解決策をまとめることができ、ほぼ正しく機能ました。小さなバグが1つあります。メッセージの最後の行がそれ自体の行に印刷されず、最後から2番目の行の直後に印刷されるだけです。

誰かがその小さな問題を修正する方法を知っているなら、私はその方法を知りたいです。

.data
    key: .byte 1
    msg:
        .ascii "ayZ B"
        .byte 10
        .ascii "234"
        .byte 10
        .ascii "b cD a"
        .byte 10 
.text
main:
    jal reverseLinesAndPrint
    # Exit Program
    li $v0, 10
    syscall

reverseLinesAndPrint:           # $s3 contains address of last char, $s0 contains offsets for both front and back, but must be reset before using for back 
    li $s0, 0                   # RESET value of msg position offset index to iterate from beginning again
    lineLoop:
    add  $s1, $t0, $s0          # Set $s1 equal to the base address of msg ($t0) + the position offset index ($s0)
    lb   $s2, ($s1)             # Deref. and move the current char into s2 for checking
    bne $s2, $zero, notLastChar # If the current char is not the last char in the msg, keep looping
        subi $s3, $s1, 1        # Subtract 1 from the ADDRESS of $s1 to get the last char ('\n') before the NULL Terminator and store it in $s3
        j lastCharIndexFound    # Exit the loop by jumping past it
    notLastChar:
    addi $s0, $s0, 1            # Increment the position offset index
    j lineLoop

    lastCharIndexFound:         # We now have the address of the last valid char in message (always '\n') stored in $s3
    li $s0, 0                   # RESET value of msg position offset index to iterate from ending this time
    reverseLineLoop:
    sub $s1, $s3, $s0           # This time, we are going to subtract from the starting address so we can iterate backwards over msg
    bne $t0, $s1, notFirstChar  # If we iterate all the way to the very first char in msg, exit the loop
        li $v0, 4               # Since the first char doesn't have a '\n' char, we have to manually print
        move $a0, $s1
        syscall
        j exit                  
    notFirstChar:
    lb $s2, ($s1)               # Deref. and move the current char into s2 for checking
    bne $s2, 10, notNLChar      # If we find a '\n' char, we need to do some logic
        li $v0, 4               # First we need to call a syscall to print string (it will stop on the previous '\n' which is now NULL)
        move $a0, $s1
        syscall
        sb $zero, ($s1)         # Second, we need to replace that current '\n' char with NULL
    notNLChar:                  # If the current char is not '\n', keep looping
    addi $s0, $s0, 1            # Increment the offset
    j reverseLineLoop           # Jump to next iteration

exit:
    jr $ra  # Jump back to main
ピーター・コーデス

以前に印刷した行から改行を区切るため、行のに改行を使用していると思いますこれは賢いアイデアであり、(前の改行なしで)行だけを印刷するよりも効率的です。それ以外の場合はsyscall$v0 = 11/ $a0 = '\n'MARSシステムコール)のように、個別のprint-single-charシステムコールを行う必要があります。

これは、各行の終わりにカーソルを置いたまま、出力が"\nline3"then"\nline2"などのようになることを意味します

ただし、最後の行(入力文字列の最初)は\n前にないため、特殊なケースにする必要がありますあなたはすでにそれ特別なケーシングにしているので\n、前の行の終わりの行として、print-char syscallを使用して、その前に手動で印刷するだけです。


これを行う別の方法は、改行の0に文字の上にを格納する1($s1)ことです。したがって、後でこの行の先頭に到達したとき"line2\n"に、末尾に改行を含めるように印刷できます。(これの私のバージョンは以下に含まれています。)

特殊なケースが入力の最終行(出力の最初の行)になり0ますが、0で終了するCスタイルの暗黙的な長さの文字列がある場合は、改行の後にバイトを格納することは実際には問題ありません。すでにそこにあるので、外側のループに入るときにそれをスキップするか、そうでない方が便利な場合はスキップすることができます。


データを変更せずに: write(1, line, length)

MARSには、write()$v0=15ポインタ+長さを取るシステムコール)があるため、文字列を0で終了する必要はありません。POSIXとまったく同じwrite(int fd, char *buf, size_t len)です。ファイル記述子$a0 = 1は、MARS4.3以降では標準出力です。

改行を見つけたら、その位置を記録してループを続けることができます。別のものを見つけたら、subu $a2, $t1, $t0($ a2 = end --start)を実行して長さを取得し$a1、改行の後の文字を指すように設定できます。

したがって、入力データを壊したり、読み取り専用入力で使用したり、後で必要なもののために破棄するためにコピーを作成したりすることなく、選択したチャンクを印刷できます。


その他のもの/コードレビュー

あなたのコードは奇妙です。reverseLinesAndPrintmainのレジスタにポインタと長さまたはエンドポインタを入れずに呼び出すのに、なぜそれをまったく別の関数にするのですか?再利用できません。

通常は、ASCIIデータのブロックの最後に別のラベルを配置して、文字列をスキャンして長さを見つけることなく、そのアドレスをレジスタに取り込むことができます。(特に0、文字列の最後に明示的に終了するバイトがないためです。直後に他のデータを配置しなかったために1つあり、MARSはメモリを使用するときにデータとコードの間にギャップを残しますデータセクションの開始アドレスをアドレス0に配置するモデル。)

そして、あなたは決して使用しませんla $reg, msgアドレスを次のようにハードコーディングしているよう0です。そして$t0、最初に初期化せずに読みますMARSは、すべてのレジスタをゼロにして開始します。(したがって、このようなバグは、選択したメモリレイアウトの有効なアドレスであるため、見逃す可能性があります。)

通常のMIPS呼び出し規約では、$sレジスターは呼び出し保存(「保存」)され、別名不揮発性です。ただし、関数はそれらを保存/復元せずに一時的なものとして使用します。$t代わりレジスタ(および$ a0..3と$ v0..1)を使用するのが普通です。

ループは非効率的ですdo{}while()条件分岐をのように下部に配置できますループを作成する方法は非常に不格好で、ループの反復ごとに2つの分岐が必要です(無条件のループ分岐を含む)。それともあなたがチェックする必要があり、検索のためのループ3\nとのためにp == end

// your loops are over-complicated like this:
do {
 loop body;
 if (p == end) {  // conditional branch over the loop epilogue
   stuff;         // put this after the loop instead of jumping over it inside the loop
   goto out;
 }
 counter increment;
} while(1);
out:

また、各レジスタの目的を示すコメントのブロックをどこかに書き込みます。一部の場合は、レジスタを初期化する命令上にある可能性があります。

一般的に、あなたのコメントはかなり良いものであり、実際の指示からすでにわかる「$ s0に1を追加する」のようなものではなく、何が起こっているのかをより高いレベルで説明しています。


これが私がそれをした方法です

印刷後に行の最初の文字を上書きするというアイデアを使用しました。これは、改行に続くバイトですしたがって、行を印刷するとき、それらはそうではline2\nないようです\nline2

またmsg、strlenループを使用する代わりに、の最後にラベルを付けました文字列を前向きに繰り返す場合は、最初に考えていたように、後で作業を保存するときに、ポインタをどこかに(スタックなどに)保存する必要があります。ただし、アセンブル時定数文字列の場合は、アセンブラーに終了先を教えてもらうだけです。また.ascii、ソースをよりコンパクトに保つ​​ために、行を1つの文字列にパックしました.byte 0(の代わりに.asciiz明示的なターミネーターを追加したので、ターミネーターの後にではなくラベルを付けることができました。

もちろん、インデックスではなくポインタを使用するのでadd、ループ内でインデックスを作成する必要はありませんでしたlbu32ビットへの符号拡張よりもゼロ拡張の方が効率的である場合に使用しました。char値は、-128..127ではなく0..255の小さな整数と考えたいと思います。平等のためだけに、署名された比較を行うわけではありません。

addiuポインタ計算で符号付きオーバーフローをトラップしたくないので、使用しました。add代わりに使用する唯一の理由adduは、署名されたオーバーフローをトラップすることです。

内側のループでは、両方の終了条件をチェックするために2つの条件付き分岐が必要ですが、これは、慎重に計画してこのようなループを作成する方法のコンパクトで効率的な例です。

.data
 msg:
    .ascii "ayZ B\n234\nb cD a\n"
 endmsg:         # 0 terminated *and* we have a label at the end for convenience.
    .byte 0

.text
main:
    la   $a0, endmsg
    la   $a1, msg
    jal reverseLinesAndPrint   # revprint(end, start)

    li $v0, 10
    syscall           # exit()

reverseLinesAndPrint:
# $a0 = end of string.  We assume it's pointing at a '\0' that follows a newline
# $a1 = start of string
# $t2 = tmp char
# we also assume the string isn't empty, i.e. that start - end >= 2 on function entry.

# the first char the inner loop looks at is -2(end)

 #move  $t0, $a0          # instead we can leave our args in a0, a1 because syscall/v0=4 doesn't modify them

 lines:                       
   findNL_loop:                    # do {  // inner loop
     addiu  $a0, $a0, -1             # --p
     beq    $a1, $a0, foundStart     # if(p==start) break
     lbu    $t2, -1($a0)                # find a char that follows a newline
     bne    $t2, '\n', findNL_loop   # }while(p[-1] != '\n');
   # $a0 points to the start of a 0-terminated string that ends with a newline.
   foundStart:
    li     $v0, 4
    syscall                        # fputs(p /*$a0*/, stdout)

    sb     $zero, ($a0)            # 0-terminate the previous line, after printing
    bne    $a0, $a1, lines         # } while(!very start of the whole string)

    jr $ra

テストされ、データを処理します。空の最初の行のようなコーナーケースではテストされていませんが、空の最後の行では機能します。すべての場合で最初の文字の前を読むことは避けていると思います(コメントの前提条件に違反する短すぎる入力を除きます。処理したい場合は、ループに入る前にそれらを確認できます)。

これbne $t2, 10, targetは疑似命令であることに注意してください。さらに最適化する場合10は、反復ごとにアセンブラーにレジスターにその定数を設定させるのではなくliinto$t3などを使用してループから引き上げます。li $v0, 4-についても同じです。syscallには戻り値がないため、破棄すらしません$v0

-1($a0)アドレッシングモードのようオフセットを使用するのは「無料」です。命令には16ビットの即時変位があるため、個別のポインタ計算の代わりに使用することをお勧めします。

は本当の理由$t2$t0はなく、MARSの小さいフォントで人間が読みやすくするために使用していたすべてのregに一意の番号を付けるために使用しました。

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

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

編集
0

コメントを追加

0

関連記事

分類Dev

複数行の文字列を複数の行に分割するにはどうすればよいですか?

分類Dev

特定の列に複数行の文字列全体を印刷するにはどうすればよいですか?

分類Dev

PowerShellの配列に複数行の文字列を追加するにはどうすればよいですか?

分類Dev

JavaScriptの複数行のコードで文字列を分割するにはどうすればよいですか?

分類Dev

ファイルの順序で複数のファイルから文字列を含む行を取得するにはどうすればよいですか?

分類Dev

複数のC ++文字列を1行に連結するにはどうすればよいですか?

分類Dev

複数のC ++文字列を1行に連結するにはどうすればよいですか?

分類Dev

整数の文字列を複数行に表示するにはどうすればよいですか?

分類Dev

複数行のStringBuilderを文字配列に変換するにはどうすればよいですか?

分類Dev

YAMLの文字列を複数行に分割するにはどうすればよいですか?

分類Dev

node.jsで複数行の文字列を作成するにはどうすればよいですか?

分類Dev

Rで複数行の文字列を作成するにはどうすればよいですか?

分類Dev

Microsoft Officeで複数行の文字列を検索するにはどうすればよいですか?

分類Dev

Python XORで複数行の文字列を復号化するにはどうすればよいですか?

分類Dev

文字列Androidの複数の行をトリミングするにはどうすればよいですか?

分類Dev

MIPSの同じ行に文字列と別の文字列変数を出力するにはどうすればよいですか?

分類Dev

1行の列を複数の行と列に分割するにはどうすればよいですか?

分類Dev

Vimで選択した行の順序を逆にするにはどうすればよいですか?

分類Dev

ファイルの行の順序を逆にするにはどうすればよいですか?

分類Dev

ファイルの行の順序を逆にするにはどうすればよいですか?

分類Dev

ファイルの行の順序を逆にするにはどうすればよいですか?

分類Dev

行が空/空白のときに複数行の文字列の最後の行を効率的に削除するにはどうすればよいですか?

分類Dev

複数の手順を連続して実行するにはどうすればよいですか?

分類Dev

複数行の文字列のすべての行から最初の40文字を削除するにはどうすればよいですか?

分類Dev

VBA-Excelで重複行と逆順の重複行を削除するにはどうすればよいですか?

分類Dev

行末の前に文字列を数文字分割するにはどうすればよいですか?

分類Dev

複数の列で値による順序付き選択を実行するにはどうすればよいですか

分類Dev

Haskellで複数行の文字列を書くにはどうすればよいですか?

分類Dev

特定の検索文字列を含む2つの中括弧の間に複数行の文字列を取得するにはどうすればよいですか?

Related 関連記事

  1. 1

    複数行の文字列を複数の行に分割するにはどうすればよいですか?

  2. 2

    特定の列に複数行の文字列全体を印刷するにはどうすればよいですか?

  3. 3

    PowerShellの配列に複数行の文字列を追加するにはどうすればよいですか?

  4. 4

    JavaScriptの複数行のコードで文字列を分割するにはどうすればよいですか?

  5. 5

    ファイルの順序で複数のファイルから文字列を含む行を取得するにはどうすればよいですか?

  6. 6

    複数のC ++文字列を1行に連結するにはどうすればよいですか?

  7. 7

    複数のC ++文字列を1行に連結するにはどうすればよいですか?

  8. 8

    整数の文字列を複数行に表示するにはどうすればよいですか?

  9. 9

    複数行のStringBuilderを文字配列に変換するにはどうすればよいですか?

  10. 10

    YAMLの文字列を複数行に分割するにはどうすればよいですか?

  11. 11

    node.jsで複数行の文字列を作成するにはどうすればよいですか?

  12. 12

    Rで複数行の文字列を作成するにはどうすればよいですか?

  13. 13

    Microsoft Officeで複数行の文字列を検索するにはどうすればよいですか?

  14. 14

    Python XORで複数行の文字列を復号化するにはどうすればよいですか?

  15. 15

    文字列Androidの複数の行をトリミングするにはどうすればよいですか?

  16. 16

    MIPSの同じ行に文字列と別の文字列変数を出力するにはどうすればよいですか?

  17. 17

    1行の列を複数の行と列に分割するにはどうすればよいですか?

  18. 18

    Vimで選択した行の順序を逆にするにはどうすればよいですか?

  19. 19

    ファイルの行の順序を逆にするにはどうすればよいですか?

  20. 20

    ファイルの行の順序を逆にするにはどうすればよいですか?

  21. 21

    ファイルの行の順序を逆にするにはどうすればよいですか?

  22. 22

    行が空/空白のときに複数行の文字列の最後の行を効率的に削除するにはどうすればよいですか?

  23. 23

    複数の手順を連続して実行するにはどうすればよいですか?

  24. 24

    複数行の文字列のすべての行から最初の40文字を削除するにはどうすればよいですか?

  25. 25

    VBA-Excelで重複行と逆順の重複行を削除するにはどうすればよいですか?

  26. 26

    行末の前に文字列を数文字分割するにはどうすればよいですか?

  27. 27

    複数の列で値による順序付き選択を実行するにはどうすればよいですか

  28. 28

    Haskellで複数行の文字列を書くにはどうすればよいですか?

  29. 29

    特定の検索文字列を含む2つの中括弧の間に複数行の文字列を取得するにはどうすればよいですか?

ホットタグ

アーカイブ