[Arm64] 演算命令

演算命令は四則演算、論理演算、比較などを行う命令群
ARM64では演算命令に3つのレジスタを指定することができる

add  x1, x2, x3           // x1 = x2 + x3
add  x1, x1, x1           // x1 = x1 + x1 = x1 * 2
add  x1, x2, x3 LSL #20   // x1 = x2 + (x3 << 20)

加減算の命令のアドレッシングモードには、単純なレジスタ指定、シフトレジスタ、イミディエート(定数指定)、 拡張レジスタの4種類があり、命令によって使えるアドレッシングが異なる

イミディエート(定数指定)

    ADD   Xd, Xn, #uimm12 {, LSL #12}
    ADD   Wd, Wn, #uimm12 {, LSL #12}

シフトレジスタ(シフト済みレジスタ指定)

    ADD   Xd, Xn, Xm {, shift #amount}
    ADD   Wd, Wn, Wm {, shift #amount}

拡張レジスタ

    ADD   Xd, Xn, Wm {,extend {#amount}}
    ADD   Xd, Xn, Xm {,extend {#amount}}

拡張するデータはbyte, halfword, word, doublewordが可能

### 加減算
Add

    ADD    Xd, Xn, Xm {,shift #amount}
    ADD    Wd, Wn, Wm {,shift #amount}

    ADD    Xd|SP,  Xn|SP,  #uimm12 {, LSL #12}
    ADD    Wd|WSP, Wn|WSP, #uimm12 {, LSL #12}

    ADD    Xd|SP,  Xn|SP,  Rm {,extend {#amount}}
    ADD    Wd|WSP, Wn|WSP, Rm {,extend {#amount}}

ADDS

    ADDS   Xd, Xn, Xm {,shift #amount}
    ADDS   Wd, Wn, Wm {,shift #amount}

    ADDS   Xd|SP,  Xn|SP,  #uimm12 {,LSL #12}
    ADDS   Wd|WSP, Wn|WSP, #uimm12 {,LSL #12}

    ADDS   Xd|SP,  Xn|SP,  Rm {,extend {#amount}}
    ADDS   Wd|WSP, Wn|WSP, Rm {,extend {#amount}}

SUB

    SUB    Xd, Xn, Xm {,shift #amount}
    SUB    Wd, Wn, Wm {,shift #amount}

    SUB    Xd|SP,  Xn|SP,  #uimm12 {,LSL #12}
    SUB    Wd|WSP, Wn|WSP, #uimm12 {,LSL #12}

    SUB    Xd|SP,  Xn|SP,  Rm {,extend {#amount}}
    SUB    Wd|WSP, Wn|WSP, Rm {,extend {#amount}}

NEG

    NEG    Xd, Xm {,shift #amount}      // SUB   Xd, XZR, Xm {,shift #amount} と同じ
    NEG    Wd, Wm {,shift #amount}      // SUB   Xd, WZR, Xm {,shift #amount} と同じ

SUBS

    SUBS   Xd, Xn, Xm {,shift #amount}
    SUBS   Wd, Wn, Wm {,shift #amount}

    SUBS   Xd|SP,  Xn|SP,  #uimm12 {, LSL #12}
    SUBS   Wd|WSP, Wn|WSP, #uimm12 {, LSL #12}

    SUBS   Xd|SP,  Xn|SP,  Rm {,extend {#amount}}
    SUBS   Wd|WSP, Wn|WSP, Rm {,extend {#amount}}

NEGS

    NEGS   Xd, Xm {,shift #amount}      // SUBS Xd, XZR, Xm {,shift #amount} と同じ 
    NEGS   Wd, Wm {,shift #amount}      // SUBS Wd, WZR, Wm {,shift #amount} と同じ 

CMP

    CMP  Wn|WSP, #imm {,shift}           // SUBS WZR, Wn|WSP, #imm {,shift} と同じ
    CMP  Xn|SP, #imm {,shift}            // SUBS XZR, Xn|SP, #imm {,shift} と同じ

    CMP  Wn|WSP, Wm {,extend {#amount}}  // SUBS WZR, Wn|WSP, Wm {,extend {#amount}} と同じ
    CMP  Xn|SP, R m {,extend {#amount}}  // SUBS XZR, Xn|SP, R m {,extend {#amount}} と同じ

    CMP  Wn, Wm {,shift #amount}         // SUBS WZR, Wn, Wm {,shift #amount} と同じ
    CMP  Xn, Xm {,shift #amount}         // SUBS XZR, Xn, Xm {,shift #amount} と同じ

CMN

  CMN   Wn|WSP, #imm {,shift}            // ADDS WZR, Wn|WSP, #imm {,shift}と同じ
  CMN   Xn|SP, #imm {,shift}             // ADDS XZR, Xn|SP, #imm {,shift}と同じ

  CMN   Wn|WSP, Wm {,extend {#amount}}   // ADDS WZR, Wn|WSP, Wm {,extend {#amount}}と同じ
  CMN   Xn|SP,  Rm {,extend {#amount}}   // ADDS XZR, Xn|SP, Rm {,extend {#amount}}と同じ

  CMN   Wn, Wm {,shift #amount}          // ADDS WZR, Wn, Wm {,shift #amount}と同じ
  CMN   Xn, Xm {,shift #amount}          // ADDS XZR, Xn, Xm {,shift #amount}と同じ

### キャリー込みの加減算
加減算でオーバーフロー (キャリー、桁あふれ) やアンダーフロー (ボロー、桁借り) のために NZCV レジスタの C ビットがキャリーとして使用される

キャリーを含めて加算。 キャリーフラグが 1 のときに 1 が余分に加算される

    ADC     Rd, Rn, Rm
    ADCS    Rd, Rn, Rm

キャリーフラグとキャリー込の減算例

.include        "debug.s"
.text
.global _start
_start:
        mov     x1, #0x8000000000000000
        adds    x0, x1, x1
        PRINTFLAGS
        adc     x0, xzr, xzr

        bl      PrintLeft
        bl      NewLine
        bl      Exit

$ ld -o test test.o
$ ./test
nZCV
1

キャリー込みの減算

    SBC     Rd, Rn, Rm
    SBCS    Rd, Rn, Rm
.include        "debug.s"
.text
.global _start

_start:
        mov     x1, #1
        subs    x0, xzr, x1
        PRINTFLAGS
        sbc     x0, xzr, xzr

        bl      PrintLeft
        bl      NewLine
        bl      Exit

$ ./test
Nzcv
-1

### 乗算命令
32または64ビットの乗算
MADD/MUL : 2つのレジスタの積 (Rn * Rm) をレジスタ Ra に加算して、結果をレジスタ Rd に格納

    MADD   Wd, Wn, Wm, Wa  // Wd = Wa + (Wn * Wm)
    MADD   Xd, Xn, Xm, Xa  // Xd = Xa + (Xn * Xm)

    MUL    Wd, Wn, Wm      // MADD Wd, Wn, Wm, WZR の別名
    MUL    Xd, Xn, Xm      // MADD Xd, Xn, Xm, XZR の別名

MSUB/MNEG : 積差(Multiply-Subtract)演算は2つのレジスタの積 (Rn * Rm) をレジスタ Ra から減算して、結果をレジスタ Rd に格納

    MSUB   Wd, Wn, Wm, Wa  // Wd = Wa - (Wn * Wm)
    MSUB   Xd, Xn, Xm, Xa  // Xd = Xa - (Xn * Xm)

    MNEG   Wd, Wn, Wm      // MSUB Wd, Wn, Wm, WZR の別名
    MNEG   Xd, Xn, Xm      // MSUB Xd, Xn, Xm, XZR の別名

SMULH : 符号付上位乗算 (Signed Multiply High) は 2 つの 64 ビットレジスタ Xn と Xm を乗算し、その 128 ビットの乗算結果の上位 64 ビットをレジスタ (Xd) に書き込み

SMULH  Xd, Xn, Xm

UMULH : 符号なし上位乗算 (Unsigned Multiply High) は 2 つの 64 ビットレジスタを乗算し、その 128 ビットの乗算結果の上位 64 ビットをレジスタに書き込み

UMULH  Xd, Xn, Xm

SMADDL/SMUL : 2 つの 32 ビットのレジスタ Wn とWm の積を 64 ビットのレジスタ Xa と加算し、その結果を 64 ビットのレジスタ Xd に書き込み

    SMADDL Xd, Wn, Wm, Xa
    SMULL  Xd, Wn, Wm      // SMADDL Xd, Wn, Wm, XZR の別名

UMADDL/UMULL : 2 つの 32 ビットのレジスタ Wn とWm の積を 64 ビットのレジスタ Xa と加算し、その結果を 64 ビットのレジスタ Xd に書き込み

    UMADDL Xd, Wn, Wm, Xa
    UMULL  Xd, Wn, Wm      // UMADDL Xd, Wn, Wm, XZR の別名

SMSUBL/SMNEGL : 64 ビットのレジスタ Xa から 32ビットレジスタWn とWm の積を減算し、その結果を 64 ビットのレジスタ Xd に書き込み

    SMSUBL Xd, Wn, Wm, Xa  // Xd = Xa - (Wn * Wm)
    SMNEGL Xd, Wn, Wm      // SMSUBL Xd, Wn, Wm, XZR の別名

UMSUBL/UMNEGL : 64 ビットのレジスタ Xa から 32ビットレジスタWn とWm の積を減算し、その結果を 64 ビットのレジスタ Xd に書き込み

    UMSUBL Xd, Wn, Wm, Xa
    UMNEGL Xd, Wn, Wm      // UMSUBL Xd, Wn, Wm, XZR の別名

### 除算命令

    Rd = Rn / Rm

    mov     x2, #37
    mov     x3, #10
    sdiv    x0, x2, x3     // x0 = 37 / 10     --> 3
    msub    x4, x0, x3, x2 // x4 = 37 - 3 * 10 --> 7

SDIV

    SDIV  Wd, Wn, Wm
    SDIV  Xd, Xn, Xm

    mov     x5, #0x8000000000000000  // -9223372036854775808
    mov     x6, #-1                  // -1
    sdiv    x0, x5, x6               // x0 = -9223372036854775808

UDIV

    UDIV  Wd, Wn, Wm
    UDIV  Xd, Xn, Xm

論理演算
論理演算命令のアドレッシングモードには、シフトレジスタ、イミディエート(定数指定)、 拡張レジスタの3種類がある。命令によって使えるアドレッシングモードが異なる。

AND

    AND Wd|WSP, Wn, #imm
    AND Xd|SP,  Xn, #imm

    AND    Wd, Wn, Wm {,shift #amount}
    AND    Xd, Xn, Xm {,shift #amount}

ANDS

    ANDS  Wd|WSP, Wn, #imm
    ANDS  Wd, Wn, Wm {,shift #amount}

    ANDS  Xd|SP, Xn, #imm
    ANDS  Xd, Xn, Xm {,shift #amount}

TST

    TST    Wn, #imm                 // ANDS   WZR, Xn, #imm と同じ
    TST    Wn, Wm {,shift #amount}  // ANDS  WZR, Wn, Wm {,shift #amount} と同じ

    TST    Xn, #imm                 // ANDS   XZR, Xn, #imm と同じ
    TST    Xn, Xm {,shift #amount}  // ANDS  XZR, Xn, Xm {,shift #amount} と同じ

ORR : レジスタとレジスタ、またはレジスタと定数のビット単位の論理和を実行

    ORR  Wd|WSP, Wn, #imm
    ORR  Wd, Wn, Wm {,shift #amount}

    ORR  Xd|SP, Xn, #imm
    ORR  Xd, Xn, Xm {,shift #amount}

ORN: Rn レジスタの値と、 Rm レジスタを反転した値との間で論理和を求め、 結果を Rd レジスタに書き込み

    ORN  Xd, Xn, Xm {,shift #amount}
    ORN  Wd, Wn, Wm {,shift #amount}

EOR: レジスタとレジスタ、またはレジスタと定数のビット単位の排他的論理和を実行

    EOR  Wd|WSP, Wn, #imm
    EOR  Wd, Wn, Wm {,shift #amount}

    EOR  Xd|SP, Xn, #imm
    EOR  Xd, Xn, Xm {,shift #amount}

EON: Rn レジスタの値と、 Rm レジスタを反転した値との間で論理和

    EON  Xd, Xn, Xm {,shift #amount}
    EON  Wd, Wn, Wm {,shift #amount}

BIC: BIC命令は、ビット単位で指定したビットをクリア

    BIC  Wd, Wn, Wm {,shift #amount}
    BIC  Xd, Xn, Xm {,shift #amount}

BICS: Rn レジスタの値と、 Rm レジスタを反転した値との間で論理積を求め、結果を Rd レジスタに書き込み

    BICS Wd, Wn, Wm {,shift #amount}
    BICS Xd, Xn, Xm {,shift #amount}