s1レジスタに-3を設定してから繰り返し領域に進み、その後s1を増やして、そのs1が0より小さければば、繰り返しの分岐を行う手法です。
(他に「3から始めて、繰り返し内で減らし、その値が0より大きければ、繰り返しの戻る分岐を行う」方法もよく使われます。
以下を少し変更することで可能です。実際に変更して試してみましょう。)
命令名と由来 | 概要 | 例 |
---|---|---|
beq (Branch on EQual) | レジスタ間の比較で、一致したら分岐 | BEQ t0,zero, L02 |
bne (Branch on Not Equal) | レジスタ間の比較で、一致しないなら分岐 | BNE t0, zero, L01 |
blez (Branch on LEss than or Equal to Zero) | 指定レジスタがゼロ以下なら分岐 | BLEZ s1,L01 |
bltz (Branch on Less Than Zero) | 指定レジスタがゼロより小さいなら分岐 | BLTZ s1,L01 |
bgez (Branch on Greater or Equal to Zero) | 指定レジスタがゼロ以上で分岐 | bgez t0, L02 |
bgtz (Branch on Greater Than Zero) | 指定レジスタがゼロより大きければ分岐 | bgtz t0, L02 |
下は、s0レジスタを制御変数に使って、s0を1,2,3と変化させて、繰り返しを制御しています。
BNEを使った場合と、BNELを使った場合の例です、
s0=0;do { □□;s0++; } while( s0 != 0) |
s0=1;do { □□; } while( s0++ != 0) |
|
---|---|---|
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <xc.h> // test.S (USBへhelloの文字列を出力するプログラム) .set noreorder #アセンブラに命令の順序を自動変更させない。 .section test_main,address(0x80005000),code .ent test_main test_main: ADDIU sp,sp,-24 # 以下で使う24byteまでの退避サイスをスタックに作る。 SW ra,20(sp) # 現在の関数の戻り番地をスタックに退避 SW fp,16(sp) # 呼び出し前の関数のフレームポインタをスタックに退避 MOVE fp,sp # 現在の関数のフレームポインタをこの関数用に更新 ADDIU s0, s0, 0 # s0に0をセット NOP L01: LUI v0,0xa000 # 以下3行で_PTR_HANDLERS[_IDX_API_SEND_STRING]内容をv0に設定 ORI v0,v0,0x41a4 LW v0,0(v0) LUI v1,%hi(.L_STR_HELLO) # v0の関数の引数を準備 ADDIU a0,v1,%lo(.L_STR_HELLO) # v0の関数の引数をa0レジスタに設定 JALR v0 # Raに戻り値を設置してv0の関数を呼び出す。 NOP ADDIU s0, s0, 1 # s0++ ADDIU t0, s0, -3 # t0 = s0 - 3 BNE t0, zero, L01 # if t0 != 0 goto L01 NOP # 直前がBranch Likely命令でない時、分岐に関係なく実行されるので入れる MOVE sp,fp # フレームポインタで退避前でspを退避前に戻す。 LW ra,20(sp) # 現在の関数の戻り番地をスタックから復元 LW fp,16(sp) # この関数の呼び出し前のフレームポインタをスタックから復元 JR ra # この関数の呼び出し後に移動(リターンに相当する) ADDIU sp,sp,24 # この関数の呼び出し前のスタックポインタに戻る .end test_main .section .L_STR_HELLO,address(0x80005058),code .align 2 .L_STR_HELLO: .ascii "hello\r\n\x00" |
#include <xc.h> // test.S (USBへhelloの文字列を出力するプログラム) .set noreorder #アセンブラに命令の順序を自動変更させない。 .section test_main,address(0x80005000),code .ent test_main test_main: ADDIU sp,sp,-24 # 以下で使う24byteまでの退避サイスをスタックに作る。 SW ra,20(sp) # 現在の関数の戻り番地をスタックに退避 SW fp,16(sp) # 呼び出し前の関数のフレームポインタをスタックに退避 MOVE fp,sp # 現在の関数のフレームポインタをこの関数用に更新 ADDIU s0, s0, 1 # s0に0をセット NOP L01: LUI v0,0xa000 # 以下3行で_PTR_HANDLERS[_IDX_API_SEND_STRING]内容をv0に設定 ORI v0,v0,0x41a4 LW v0,0(v0) LUI v1,%hi(.L_STR_HELLO) # v0の関数の引数を準備 ADDIU a0,v1,%lo(.L_STR_HELLO) # v0の関数の引数をa0レジスタに設定 JALR v0 # Raに戻り値を設置してv0の関数を呼び出す。 NOP ADDIU t0, s0, -3 # t0 = s0 - 3 BNEL t0, zero, L01 # if t0 != 0 goto L01 ADDIU s0, s0, 1 # s0++ MOVE sp,fp # フレームポインタで退避前でspを退避前に戻す。 LW ra,20(sp) # 現在の関数の戻り番地をスタックから復元 LW fp,16(sp) # この関数の呼び出し前のフレームポインタをスタックから復元 JR ra # この関数の呼び出し後に移動(リターンに相当する) ADDIU sp,sp,24 # この関数の呼び出し前のスタックポインタに戻る .end test_main .section .L_STR_HELLO,address(0x80005058),code .align 2 .L_STR_HELLO: .ascii "hello\r\n\x00" |
下は、s0レジスタを制御変数に使って、s0を1,2,3と変化させて、繰り返しを制御しています。
BNEを使った場合と、BNELを使った場合の例です、
s0=0; while(s0 < 3) { s0++ } または、for(s0=0; s0 < 3; s0++){ } |
s0=1; while(s0 <= 3) { s0++ } または、for(s0=0; s0 <= 3; s0++){ } |
|
---|---|---|
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include <xc.h> // test.S (USBへhelloの文字列を出力するプログラム) .set noreorder #アセンブラに命令の順序を自動変更させない。 .section test_main,address(0x80005000),code .ent test_main test_main: ADDIU sp,sp,-24 # 以下で使う24byteまでの退避サイスをスタックに作る。 SW ra,20(sp) # 現在の関数の戻り番地をスタックに退避 SW fp,16(sp) # 呼び出し前の関数のフレームポインタをスタックに退避 MOVE fp,sp # 現在の関数のフレームポインタをこの関数用に更新 ADDIU s0, zero, 0 # s0に0をセット L01: SLTI t0, s0, 3 # if s0 < 3 : t0=1 else t0=0 BEQ t0,zero, L02 # または「beqz t0,L02」で繰り返し終了分岐 NOP LUI v0,0xa000 # 以下3行で_PTR_HANDLERS[_IDX_API_SEND_STRING]内容をv0に設定 ORI v0,v0,0x41a4 LW v0,0(v0) LUI v1,%hi(.L_STR_HELLO) # v0の関数の引数を準備 ADDIU a0,v1,%lo(.L_STR_HELLO) # v0の関数の引数をa0レジスタに設定 JALR v0 # Raに戻り値を設置してv0の関数を呼び出す。 NOP ADDIU s0, s0, 1 # s0++ J L01 NOP L02: MOVE sp,fp # フレームポインタで退避前でspを退避前に戻す。 LW ra,20(sp) # 現在の関数の戻り番地をスタックから復元 LW fp,16(sp) # この関数の呼び出し前のフレームポインタをスタックから復元 JR ra # この関数の呼び出し後に移動(リターンに相当する) ADDIU sp,sp,24 # この関数の呼び出し前のスタックポインタに戻る .end test_main .section .L_STR_HELLO,address(0x80005060,code .align 2 .L_STR_HELLO: .ascii "hello\r\n\x00" |
#include <xc.h> // test.S (USBへhelloの文字列を出力するプログラム) .set noreorder #アセンブラに命令の順序を自動変更させない。 .section test_main,address(0x80005000),code .ent test_main test_main: ADDIU sp,sp,-24 # 以下で使う24byteまでの退避サイスをスタックに作る。 SW ra,20(sp) # 現在の関数の戻り番地をスタックに退避 SW fp,16(sp) # 呼び出し前の関数のフレームポインタをスタックに退避 MOVE fp,sp # 現在の関数のフレームポインタをこの関数用に更新 ADDIU s0, zero, 1 # s0に1をセット L01: ADDIU t1, zero, 3 # t1に3をセット SUB t0, s0, t1 # t0 = s0 - t1 # bgez t0, L02 # t0が0以上で、繰り返し終了(s0-3>=0で分岐)で2回の繰り返し bgtz t0, L02 # t0が0より大きい時、繰り返し終了(s0-3>0で分岐) NOP LUI v0,0xa000 # 以下3行で_PTR_HANDLERS[_IDX_API_SEND_STRING]内容をv0に設定 ORI v0,v0,0x41a4 LW v0,0(v0) LUI v1,%hi(.L_STR_HELLO) # v0の関数の引数を準備 ADDIU a0,v1,%lo(.L_STR_HELLO) # v0の関数の引数をa0レジスタに設定 JALR v0 # Raに戻り値を設置してv0の関数を呼び出す。 NOP ADDIU s0, s0, 1 # s0++ J L01 NOP L02: MOVE sp,fp # フレームポインタで退避前でspを退避前に戻す。 LW ra,20(sp) # 現在の関数の戻り番地をスタックから復元 LW fp,16(sp) # この関数の呼び出し前のフレームポインタをスタックから復元 JR ra # この関数の呼び出し後に移動(リターンに相当する) ADDIU sp,sp,24 # この関数の呼び出し前のスタックポインタに戻る .end test_main .section .L_STR_HELLO,address(0x80005060,code .align 2 .L_STR_HELLO: .ascii "hello\r\n\x00" |