PowerPCとは、Apple, IBM, Motorolaによって開発されたRISCアーキテクチャ
AppleのMacintoshやIBMのRS/6000などで採用
ゲーム機、組み込みシステム、スーパーコンピュターで使用
現在は新規採用されるケースは少ない
Month: January 2024
[Arm64] 浮動小数点型命令
ARM64は32bit の ARMと異なり、浮動小数点数演算用の命令がオプションではなく、ARMv8 の標準として組み込まれている。
浮動小数点数の正負を表す符号(sign)、浮動小数点数の数値自体(有効数字)を表す仮数部(fraction)とべき乗を示す指数部(exponent) に分けて考える。
ARMv8は、浮動小数点数レジスタとして128ビットを使って複数のベクトル の要素を同時に演算する事ができる Vn レジスタを32本持っている。
浮動小数点数レジスタとメモリ間の読み書きは、汎用レジスタとメモリ間と 同じように簡単に行なえる。
浮動小数点数を複数格納できるベクトルレジスタ (Vn) は、 データのサイズと数をレジスタ名の後ろに指定することができる。
ベクトルレジスタ (Vn) に格納された複数の浮動小数点数の1つを指定するには、 ゼロから始まるインデックスを付けて配列形式で行う。
行列 M の16個の要素をメモリ中に { m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15 } の順に格納
1.2345678987654321 3ff-3c0ca44de3938 -1.2345678987654321 bff-3c0ca44de3938 -9.87654E33 c6f-e6f375e71abf3 9.87654E33 46f-e6f375e71abf3 5.87654E33 46f-21bc5cd4e4725 -1.87654E-33 b92-37cb2f215b389 1.2345678987654321E299 7e0-798ba335bafbd -1.2345678987654321E-299 81e-0891ed3843971 123.8987654E1 409-35bf35b91f70e 1.2345678987653 3ff-3c0ca44de36e5 0.12345678987652 3fb-f9add3afd21d2 -123456789876543 c2d-c122186c3cfc0 -123456789876542 c2d-c122186c3cf80 -123456789876541 c2d-c122186c3cf40 1.000 3ff-0000000000000 0.1 3fb-999999999999a 0 000-0000000000000
浮動小数点数の演算は、基本的にメモリに格納されている数値をレジスタに読み込んで、演算し、メモリに書き戻すという動きになる
ARM64 (ARMv8) では浮動小数点数を1つずつ扱うスカラー型と、複数の浮動小数点数をまとめて扱うベクトル型のレジスタを扱う命令が別に存在
[Arm64] 分岐命令
CPU はメモリに格納されている32ビット (4バイト) の命令をメモリアドレスの 小さい方から大きい方に向かって順に実行する。
この命令の実行順を変更する 命令が分岐命令。 実行中の命令のメモリアドレスはプログラムカウンタ (PC) が保持していますが、分岐命令はプログラムカウンタの値を書き換える命令と 考えられる。
実行するメモリアドレスに無条件にジャンプする 「B」と「BR」、呼び出し元(ジャンプした次の命令)に戻る準備をして分岐する 「BL」と「BLR」、呼び出し元へジャンプして 戻る「RET」、条件フラグの値によって分岐したり、直後の命令を順に実行したりする 条件分岐命令(B.EQ、B.GTなど) がある。
## 無条件分岐命令
### B
分岐命令の B は、プログラムカウンタの値にオフセットを加えたメモリアドレスに 分岐(ジャンプ)。 分岐先の命令に付けたラベルを分岐先として指定しますが、 アセンブラが「B」命令の アドレスとラベルのアドレスから、プログラムカウンタ相対オフセットを計算
B label
### BR
分岐命令の BR は、分岐先のメモリアドレスを汎用レジスタに格納して実行することで 、汎用レジスタが格納しているメモリアドレスへの無条件分岐
BR Xn
### BL
リンク付分岐命令の BL は、プログラムカウンタの値にオフセットを加えた メモリアドレスに分岐(ジャンプ)。 分岐先の命令に付けたラベルを分岐先として指定しますが、 アセンブラが「BL」命令のアドレスとラベルのアドレスから、 プログラムカウンタ相対オフセットを計算
BL label
### BLR
リンク付分岐命令の BLR は、分岐先のメモリアドレスを汎用レジスタに格納して 実行することで、汎用レジスタが格納しているメモリアドレスへの無条件分岐
BLR Xn
### RET
RET (Return) 命令は、指定したレジスタのアドレスに無条件分岐。 レジスタを指定しない場合はリンクレジスタ(X30) を指定したことになる
RET { Xn }
### サブルーチン呼び出し
// 呼び出し元 Main: 略 bl Sub // サブルーチン呼び出し mov x0, xzr // X30に格納される戻り先 略 // サブルーチン Sub: // このラベルにジャンプ stp x0, x30, [sp, #-16]! // リンクレジスタ(X30)の退避 stp x1, x2, [sp, #-16]! // レジスタの PUSH の例 // 何か処理 ldp x1, x2, [sp], #16 // レジスタの POP の例 ldp x0, x30, [sp], #16 // リンクレジスタ(X30)の復帰 ret // 呼び出し元の次の命令に戻る
### 条件分岐命令
EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV
### CBZ
CBZ Wt, label CBZ Xt, label
### CBNZ
汎用レジスタ(Rt)の値が 0 でなければ、ラベルに分岐
CBNZ Wt, label CBNZ Xt, label
### TBZ
TBZ(Test bit and Branch if Zero)命令は汎用レジスタ(Rt)の imm で指定した ビット(0..63) が 0 ならばラベルに分岐
TBZ Wt, #imm, label TBZ Xt, #imm, label
### TBNZ
TBNZ (Test bit and Branch if Nonero) 命令は汎用レジスタ(Rt)の imm で指定した ビット(0..63) が 1 ならばラベルに分岐
TBNZ Wt, #imm, label TBNZ Xt, #imm, label
## 条件実行命令
### CSEL
条件が真ならば Rd に Rn を返します。条件が偽ならば Rd に Rm を返します。 条件はサフィックスで指定します。
CSEL Wd, Wn, Wm, 条件 CSEL Xd, Xn, Xm, 条件
### CSET
条件が真ならば Rd に 1 を返し、偽ならば Rd に 0
CSET Wd, 条件 // CSINC Wd, WZR, WZR, 逆条件 CSET Xd, 条件 // CSINC Xd, XZR, XZR, 逆条件
### CSINC
条件が真ならば Xd に Rn
CSINC Wd, Wn, Wm, 条件 CSINC Xd, Xn, Xm, 条件
### CSINV
条件が真ならば Rd に Rn を返します。 条件が偽ならば Rm のビット毎の反転(1の補数)を Rd に
CSINV Wd, Wn, Wm, 条件 CSINV Xd, Xn, Xm, 条件
### CSNEG
条件が真ならば Rd に Rn を返します。 条件が偽ならば Rm が符号付整数として2の補数(正負反転)を Rd に
CSINV Wd, Wn, Wm, 条件 CSINV Xd, Xn, Xm, 条件
## 条件比較
### CCMP (イミディエート)
指定した条件(サフィックス)が真の場合は、 汎用レジスタと5ビットの符号なし整数 (0..31) を比較した結果で 条件フラグを設定し、指定した条件(サフィックス)が 偽の場合は、4ビットのNZCV定数(0..15)を条件フラグに設定
// NZCV = if cond then CMP(Wn, uimm) else nzcv. CCMP Wn, #imm, #nzcv, cond CCMP Xn, #imm, #nzcv, cond
### CCMP (レジスタ)
指定した条件(サフィックス)が真の場合は、 汎用レジスタ Rn と Rm を比較した結果で条件フラグを設定し、 指定した条件(サフィックス)が偽の場合は、 4ビットのNZCV定数(0..15)を条件フラグに設定
// NZCV = if cond then CMP(Rn,Rm) else uimm4. CCMP Wn, Wm, #nzcv, cond CCMP Xn, Xm, #nzcv, cond
### CCMN (イミディエート)
指定した条件(サフィックス)が真の場合は、 汎用レジスタと、5ビットの符号なし整数 (0..31) に -1 を乗じた数を比較した結果で 条件フラグを設定し、指定した条件(サフィックス)が 偽の場合は、4ビットのNZCV定数(0..15)を条件フラグに設定
// NZCV = if cond then CMP(Xn,-uimm5) else uimm4. CCMN Wn, #imm, #nzcv, cond CCMN Xn, #imm, #nzcv, cond
### CCMN (レジスタ)
指定した条件(サフィックス)が真の場合は、 汎用レジスタ Rn と、Rm に-1を乗じた数を比較した結果で条件フラグを設定し、 指定した条件(サフィックス)が偽の場合は、 4ビットのNZCV定数(0..15)を条件フラグに設定
// NZCV = if cond then CMP(Rn,-Rm) else uimm4. CCMN Wn, Wm, #nzcv, cond CCMN Xn, Xm, #nzcv, cond
### ビット操作
REV, REV16, REV32, RBIT, CLS, CLZ
### ステータスレジスタの操作
MRS, MSR
### システムコール
SVC (Supervisor Call) 命令はシステムコール用の命令
SVC #0
.text .global _start _start: mov x2, #13 // x2 length adr x1, msg // x1 string address mov x0, #1 // x0 stdout mov x8, #64 // sys_write svc #0 mov x0, xzr mov x8, #93 // sys_exit svc #0 msg: .asciz "hello, world\n"
[Arm64] シフト演算命令
シフト演算とは論理演算と同じようにビット列を操作する演算
単純な左右へのシフトの他に、複雑な動作をするビットフィールド移動命令がある
### シフト演算
.include "debug.s" .text .global _start num: .quad 0x876543210fedcba9 _start: ldr x2, num mov x0, x2 bl PrintHex16 bl NewLine mov x1, #22 bl PrintRight bl NewLine lsl x0, x2, #8 bl PrintHex16 bl NewLine mov x1, #22 bl PrintRight bl NewLine bl Exit
$ ./test
876543210FEDCBA9
-8690466096661279831
6543210FEDCBA900
7296712173568108800
算術右シフトはASR x0, x2, #8
論理右シフトはLSR x0, x2, #8
右ローテートはROR x0, x2, #8
左ローテートは右ローテート(ROR)のシフト量を (64 – ビット数) とすることで左ローテートを実現
### アドレッシングモード
シフト演算命令には、シフト量を定数で指定するイミディエートとレジスタで指定するモードの2種類のアドレッシングモードがある
LSR Wd, Wn, #imm6 LSR Xd, Xn, #imm6 LSR Wd, Wn, Wm LSR Xd, Xn, Xm
### ASR
ASR命令 (Arithmetic Shift Right) は算術右シフトを実行。算術右シフトはRn レジスタの内容を Rm レジスタで指定したビット数だけ右にシフト。
ASR Wd, Wn, Wm // ASRV Wd, Wn, Wm と同じ ASR Xd, Xn, Xm // ASRV Xd, Xn, Xm と同じ
### LSR
LSR命令 (Logical Shift Right)は論理右シフトを実行。論理右シフトはレジスタ Rn の内容をレジスタ Rm で指定したビット数だけ右にシフトしてレジスタ Rd に返却。
LSR Wd, Wn, Wm // LSRV Wd, Wn, Wm と同じ LSR Xd, Xn, Xm // LSRV Xd, Xn, Xm と同じ LSR Wd, Wn, #shift // UBFM Wd, Wn, #shift, #31 LSR Xd, Xn, #shift // UBFM Xd, Xn, #shift, #63
### LSL
LSL命令 (Logical Shift Left)は論理左シフトを実行。論理左シフトはレジスタ Rn の内容をレジスタ Rm で指定したビット数だけ左にシフトしてレジスタ Rd に返却
LSL Wd, Wn, Wm // LSLV Wd, Wn, Wm と同じ LSL Xd, Xn, Xm // LSLV Xd, Xn, Xm と同じ LSL Wd, Wn, #shift // UBFM Wd, Wn, #(-shift MOD 32), #(31-shift) LSL Xd, Xn, #shift // UBFM Xd, Xn, #(-shift MOD 64), #(63 - shift)
### ROR
レジスタ Rn の内容をレジスタ Rm で指定したビット数(レジスタのビット数で除算した剰余)でローテイトした値をレジスタ Rd に返却
ROR Wd, Wn, Wm // RORV Wd, Wn, Wm と同じ ROR Xd, Xn, Xm // RORV Xd, Xn, Xm と同じ ROR Wd, Ws, #shift // EXTR Wd, Ws, Ws, #shift ROR Xd, Xs, #shift // EXTR Xd, Xs, Xs, #shift
### EXTR
レジスタ Wn とレジスタ Wm のペアを連結してデータのビット列を抽出。定数には抽出する最下位のビット位置を指定
EXTR Wd, Wn, Wm, #lsb EXTR Xd, Xn, Xm, #lsb
実装例
num0: .quad 0xabcdefabcdefabcd num1: .quad 0x1234567890123456 // ldr x11, num0 ldr x12, num1 // extr x0, X11, x12, #8 // CD12345678901234 extr x0, X11, x12, #16 // ABCD123456789012 extr x0, X11, x12, #24 // EFABCD1234567890
### ビットフィールドムーブ命令
レジスタ内の最下位のビットを含む連続したビット列(最も右側のビット列)を移動する命令。 移動先レジスタをゼロクリアして 移動するビット列の最上位を符号フラグとして移動先レジスタの 上位ビットに符号をコピーする SBFM 命令、 移動先のレジスタの内容の対応するビット列だけ上書きして元の内容を保持する BFM 命令、移動先レジスタをゼロクリアしてビット列だけ を書き込む UBFM 命令がある。
x2 : 923486789ABCDEF0 x3 : 0123456789ABCDEF (使われない) SBFM X3, X2, #40, #23 --> x3 : FFFFBCDEF0000000 x2 : 923486789ABCDEF0 x3 : BBBBBBBBBBBBBBBB (保持される) BFM X3, X2, #40, #23 --> x3 : BBBBBCDEF0BBBBBB x2 : 923486789ABCDEF0 x3 : 0123456789ABCDEF (使われない) UBFM X3, X2, #40, #23 --> x3 : 0000BCDEF0000000
### SBFM
Signed Bitfield Move の略で、ソースレジスタ(Wn、Xn)の 下位ビット側の任意の桁数 (imms+1) を、転送先レジスタの右シフト (immr)するビット位置にコピー
SBFM Wd, Wn, #immr, #imms SBFM Xd, Xn, #immr, #imms
x2 : 823456789ABCDEF0 x5 : 5555555555555555 SBFM X5, X2, #60, #23 --> FFFFFFFFFBCDEF00 SBFM X5, X2, #56, #23 --> FFFFFFFFBCDEF000 SBFM X5, X2, #60, #47 --> 00056789ABCDEF00 SBFM X5, X2, #56, #47 --> 0056789ABCDEF000 SBFM X5, X2, #24, #47 --> 000000000056789A SBFM X5, X2, #16, #23 --> FFFFFFFFFFFFFFBC SBFM X5, X2, #8, #23 --> FFFFFFFFFFFFBCDE
### ASR
ASR 命令 (Arithmetic Shift Right) は転送元レジスタの内容を定数で指定したビット数だけ右にシフトして転送先レジスタにコピー
ASR Wd, Wn, #shift // SBFM Wd, Wn, #shift, #31 ASR Xd, Xn, #shift // SBFM Xd, Xn, #shift, #63
### SBFIZ
SBFIZ (Signed Bitfield Insert in Zero) 命令は、転送元レジスタの最下位ビットのビット番号が lsb、幅 が width のビット列を転送先レジスタの下位にコピー
SBFIZ Xd, Xn, #lsb, #width // SBFM Wd, Wn, #(-lsb MOD 32), #(width-1) SBFIZ Wd, Wn, #lsb, #width // SBFM Xd, Xn, #(-lsb MOD 64), #(width-1)
### SBFX
SBFX (Signed Bitfield Extract) 命令は、転送元レジスタの最下位ビットのビット番号が lsb、幅 が width のビット列を取り出して符号拡張して転送先レジスタにコピー
SBFX Wd, Wn, #lsb, #width // SBFM Wd, Wn, #lsb, #(lsb+width-1) SBFX Xd, Xn, #lsb, #width // SBFM Xd, Xn, #lsb, #(lsb+width-1)
### SXTB
SXTB (Signed Extend Byte) 命令は転送元レジスタの下位8ビットを符号拡張して転送先レジスタにコピー
SXTB Wd, Wn // SBFM Wd, Wn, #0, #7 SXTB Xd, Wn // SBFM Xd, Xn, #0, #7
### SXTH
SXTH (Sign Extend Halfword) 命令は転送元レジスタの下位16ビットを符号拡張して転送先レジスタにコピー
SXTH Wd, Wn // SBFM Wd, Wn, #0, #15 SXTH Xd, Wn // SBFM Xd, Xn, #0, #15
### SXTW
SXTW (Sign Extend Word) 命令は転送元レジスタの下位32ビットを符号拡張して転送先レジスタにコピー
SXTW Xd, Wn // SBFM Xd, Xn, #0, #31
### BFM
この命令は Bitfield Move の略で、ソースレジスタ(Wn、Xn)の 下位ビット側の任意の桁数 (imms+1) を、転送先レジスタの 右シフト(immr)したビット位置にコピー
BFM Wd, Wn, #imm6r, #imm6s // sf = 0 && N = 0 BFM Xd, Xn, #imm6r, #imm6s // sf = 1 && N = 1
x2 : 823456789ABCDEF0 x5 : 5555555555555555 BFM X5, X2, #60, #23 --> 555555555BCDEF05 BFM X5, X2, #56, #23 --> 55555555BCDEF055 BFM X5, X2, #60, #47 --> 55556789ABCDEF05 BFM X5, X2, #56, #47 --> 5556789ABCDEF055 BFM X5, X2, #24, #47 --> 555555555556789A BFM X5, X2, #16, #23 --> 55555555555555BC BFM X5, X2, #8, #23 --> 555555555555BCDE
### BFI
<--- width ---> before -----------------bbbbbbbbbbbbbbb <--- width ---> lsb after ------------bbbbbbbbbbbbbbb----- BFI Wd, Wn, #lsb, #width // BFM Wd, Wn, #(-lsb MOD 32), #(width-1) BFI Xd, Xn, #lsb, #width // BFM Xd, Xn, #(-lsb MOD 64), #(width-1)
### BFXIL
BFXIL命令 (Bitfield eXtract and Insert at Low end) は、転送元レジスタの最下位ビットのビット番号が lsb 幅 が width のビット列を転送先レジスタの下位にコピー
<--- width ---> lsb before ------------bbbbbbbbbbbbbbb----- <--- width ---> after -----------------bbbbbbbbbbbbbbb BFXIL Wd, Wn, #lsb, #width // BFM Wd, Wn, #lsb, #(lsb+width-1) BFXIL Xd, Xn, #lsb, #width // BFM Xd, Xn, #lsb, #(lsb+width-1)
### UBFM
UBFM Wd, Wn, #imm6r, #imm6s // sf = 0 && N = 0 UBFM Xd, Xn, #imm6r, #imm6s // sf = 1 && N = 1 x2 : 823456789ABCDEF0 x5 : 5555555555555555 UBFM X5, X2, #60, #23 --> 000000000BCDEF00 UBFM X5, X2, #56, #23 --> 00000000BCDEF000 UBFM X5, X2, #60, #47 --> 00056789ABCDEF00 UBFM X5, X2, #56, #47 --> 0056789ABCDEF000 UBFM X5, X2, #24, #47 --> 000000000056789A UBFM X5, X2, #16, #23 --> 00000000000000BC UBFM X5, X2, #8, #23 --> 000000000000BCDE
### LSL
LSL Wd, Wn, #shift // UBFM Wd, Wn, #(-shift MOD 32), #(31-shift) LSL Xd, Xn, #shift // UBFM Xd, Xn, #(-shift MOD 64), #(63-shift)
### LSR
LSR Wd, Wn, #shift // UBFM Wd, Wn, #shift, #31 LSR Xd, Xn, #shift // UBFM Xd, Xn, #shift, #63
### UBFIZ
UBFIZ (Unsigned Bitfield Insert in Zero) 命令は転送先レジスタを0にした後、転送元レジスタの最下位ビットのビット番号が lsb、幅 が width のビット列を転送先レジスタの下位にコピー
UBFIZ Wd, Wn, #lsb, #width // UBFM Wd, Wn, #(-lsb MOD 32), #(width-1) UBFIZ Xd, Xn, #lsb, #width // UBFM Xd, Xn, #(-lsb MOD 64), #(width-1)
### UBFX
UBFX (Unsigned Bitfield Extract) 命令は転送先レジスタを0にした後、、転送元レジスタの最下位ビットのビット番号が lsb、幅 が width のビット列を転送先レジスタの下位にコピー。コピーするビット列をゼロ拡張して転送する動作となる
UBFX Wd, Wn, #lsb, #width // UBFM Wd, Wn, #lsb, #(lsb+width-1) UBFX Xd, Xn, #lsb, #width // UBFM Xd, Xn, #lsb, #(lsb+width-1)
### UXTB
UXTB (Unsigned Extend Byte) 命令は転送元レジスタの下位8ビットをゼロ拡張して転送先レジスタにコピー
UXTB Wd, Wn // UBFM Wd, Wn, #0, #7
### UXTH
UXTH (Sign Extend Halfword) 命令は転送元レジスタの下位16ビットをゼロ拡張して転送先レジスタにコピー
UXTH Wd, Wn // UBFM Wd, Wn, #0, #15
[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}
[Arm64] MOV命令
レジスタに格納されているデータを別のレジスタに格納
SP-汎用レジスタ間
MOV Wd|WSP, Wn|WSP // ADD Wd|WSP, Wn|WSP, #0 の別名 MOV Xd|SP, Xn|SP // ADD Xd|SP, Xn|SP, #0 の別名
汎用レジスタ間
MOV Wd, Wm // ORR Wd, WZR, Wm の別名 MOV Xd, Xm // ORR Xd, XZR, Xm の別名
定数
// x0 <-- 0x0123456789ABCDEF movz x0, #0xCDEF movk x0, #0x89AB, LSL #16 movk x0, #0x4567, LSL #32 movk x0, #0x0123, LSL #48
ビットマスクイミディエート
MOV Wd|WSP, #imm // ORR Wd|WSP, WZR, #imm の別名 MOV Xd|SP, #imm // ORR Xd|SP, XZR, #imm の別名
MOVK
MOVK Wd, #imm16 {,LSL #shift} MOVK Xd, #imm16 {,LSL #shift}
16 ビットの定数をビット反転してレジスタに設定
MOVN Wd, #uimm16 {,LSL #shift} MOVN Xd, #uimm16 {,LSL #shift}
16 ビットの定数をビット反転してレジスタに設定
命令エンコードはzが1ならば64ビットレジスタ、sft は 0, 16, 32, 48 のシフト量に対応
レジスタに定数を設定する MOV命令は、定数値によりアセンブラが ORR、MOVZ、MOVN、MOVK を選択して使う
[Arm64] ストア命令
ストア命令(STR)はレジスタに格納されている値をメモリに書き込む
転送元 Wt, Xt
命令 STR, STR, STRH, STRB
### (1)STR ダブルワード(64ビット) → 64ビット
64ビットレジスタのデータをメモリにコピー
STR Xt, [base], #simm9 // ポストインデックス STR Xt, [base, #simm9]! // プレインデックス STUR Xt, [base {,#simm9}] STR Xt, [base {,#uimm12}] STR Xt, [base, Wm {,SXTW|UXTW {#0 | #3}} ] // レジスタオフセット STR Xt, [base, Xm {,LSL|SXTX {#0 | #3}} ] // レジスタオフセット
### (2)STR ワード(32ビット) → 32ビット
STR Wt, [base], #simm9 // ポストインデックス STR Wt, [base, #simm9]! // プレインデックス STUR Wt, [base {,#simm9} ] STR Wt, [base {,#imm12} ] STR Wt, [base, Wm {,SXTW|UXTW {#0 | #2}} ] // レジスタオフセット STR Wt, [base, Xm {,LSL|SXTX {#0 | #2}} ] // レジスタオフセット
### (3)STRH ハーフワード(32ビット) → 16ビット
STRH Wt, [base], #simm9 // ポストインデックス STRH Wt, [base, #simm9]! // プレインデックス STURH Wt, [base {,#simm9}] STRH Wt, [base {,#uimm12}] // 符号なしオフセット STRH Wt, [base, Wm {,SXTW|UXTW {#0 | #1}} ] // レジスタオフセット STRH Wt, [base, Xm {,LSL|SXTX {#0 | #1}} ] // レジスタオフセット
### (4)STRB バイト(32ビット) → 8ビット
STRB Wt, [base], #simm9 // ポストインデックス STRB Wt, [base, #simm9]! // プレインデックス STURB Wt, [base {,#simm9}] STRB Wt, [base {,#uimm12}] // 符号なしオフセット STRB Wt, [base, Wm {,SXTW|UXTW {#0}} ] // レジスタオフセット STRB Wt, [base, Xm {,LSL|SXTX {#0}} ] // レジスタオフセット
### PC相対アドレス計算
ラベル位置のアドレスをレジスタに設定
ADR, ADRP
ADR Xd, label ADRP Xd, label
### レジスタペアのロード/ストア命令
LDP Xt1, Xt2, [base, #simm7]! // プレインデックス LDP Wt1, Wt2, [base, #simm7]! // プレインデックス // プレインデックス base = base + simm7; Rt1 = memory[base]; Rt2 = memory[base + 4|8]; LDP Xt1, Xt2, [base], #simm7 // ポストインデックス LDP Wt1, Wt2, [base], #simm7 // ポストインデックス // ポストインデックス Rt1 = memory[base]; Rt2 = memory[base + 4|8]; base = base + simm7; LDP Xt1, Xt2, [base, #simm7] // base への書き戻しなし LDP Wt1, Wt2, [base, #simm7] // base への書き戻しなし Rt1 = memory[base]; Rt2 = memory[base + simm7];
LDPは連続したメモリから2つのレジスタに読み込む命令
LDP Xt1, Xt2, [base], #simm7 LDP Wt1, Wt2, [base], #simm7 LDP Xt1, Xt2, [base, #simm7]! LDP Wt1, Wt2, [base, #simm7]! LDP Xt1, Xt2, [Xn {, #simm7}] LDP Wt1, Wt2, [Xn {, #simm7}]
STP命令は二つのレジスタの内容(Xt1, Xt2)をメモリアドレスを保持するレジスタ (Xn) にオフセットの値を加えたアドレスを先頭とするメモリに書き込み、レジスタをスタックに退避する場合によく使う
STP Xt1, Xt2, [base], #simm7 STP Wt1, Wt2, [base], #simm7 STP Xt1, Xt2, [base, #simm7]! STP Wt1, Wt2, [base, #simm7]! STP Xt1, Xt2, [base {, #simm7}] STP Wt1, Wt2, [base {, #simm7}]
[Arm64] ロード命令
ロード命令(LDR)はメモリに格納されている値をレジスタに読み込み、ストア命令はレジスタに入っている値をメモリに書き出す
LDR : メモリからレジスタ
STR : レジスタからストア
MOV : レジスタからレジスタ
ロード/ストア命令は読み書きするメモリの位置を指定する必要があり、アドレッシングという
x86, x64, ARM, PowerPCによってアドレッシングの方法が異なる
ARM64ではイミディエートオフセットと レジスターオフセットなどがある。
ベースレジスタのオフセット(加算)を使用する
イミディエートオフセットは定数、レジスターオフセットはレジスタの格納された値
LDR Xt, [base, #simm9] base = base + simm9; Xt = memory[base];
LDR Xt, [base, #uimm12] Xt = [base + uimm12 * scale];
### レジスタオフセット
ベースレジスタ(Xn または SP)が保持しているメモリアドレスにインデックス用のレジスタが格納しているオフセット値を加えたメモリアドレスをXtにコピー
LDR Xt, [base, Wm {, SXTW|UXTW {#0|#3}}] LDR Xt, [base, Xm {, LSL|SXTX {#0|#3}}] Xt = memory[base + {* 8}];
### ロード命令
メモリが64, 32, 16, 8ビットあり、レジスタが32ビット(Wt)と64ビット(Xt)があるため、ビットに応じてそれぞれ命令がある
ビット幅を増やすには符号拡張とするか、ゼロ拡張を行う
### ロード命令の詳細
6種類のアドレッシング
9ビット符号付即値+ポストインデックス
9ビット符号付即値+プレインデックス
9ビット符号付即値
12ビット符号なし即値
32ビットレジスタオフセット
64ビットレジスタオフセット
### (1)LDR ダブルワード(64ビット) → 64ビット
LDR Xt, [base], #simm9 // ポストインデックス LDR Xt, [base, #simm9]! // プレインデックス LDUR Xt, [base, #simm9] LDR Xt, [base {,#uimm12} ] // 符号なしオフセット(8の倍数) [0 - 32760] LDR Xt, [base, Wm {,SXTW|UXTW {#0 | #3}} ] // レジスタオフセット LDR Xt, [base, Xm {,LSL|SXTX {#0 | #3}} ] // レジスタオフセット
### (2)LDR ワード(32ビット) → 32ビット
LDR Wt, [base], #simm9 // ポストインデックス LDR Wt, [base, #simm9]! // プレインデックス LDUR Wt, [base, #simm9] LDR Wt, [base {,#uimm12} ] // 符号なしオフセット(4の倍数) [0 - 16380] LDR Wt, [base, Wm {,SXTW|UXTW {#0 | #2}} ] LDR Wt, [base, Xm {,LSL|SXTX {#0 | #2}} ]
### (3)LDRSW 符号付きワード(32ビット) → 64ビット
LDRSW Xt, [base], #simm9 // ポストインデックス LDRSW Xt, [base, #simm9]! // プレインデックス LDURSW Xt, [base, #simm9] LDRSW Xt, [base {,#uimm12} ] // 符号なしオフセット(4の倍数) [0 - 16380] LDRSW Xt, [base, Wm {,SXTW|UXTW {#0 | #2}} ] LDRSW Xt, [base, Xm {,LSL|SXTX {#0 | #2}} ]
### (4)LDRH ハーフワード(16ビット) → 32ビット
LDRH Wt, [base], #simm9 // ポストインデックス LDRH Wt, [base, #simm9]! // プレインデックス LDURH Wt, [base, #simm9] LDRH Wt, [base {,#uimm12} ] // 符号なしオフセット(2の倍数) [0 - 8190] LDRH Wt, [base, Wm {,SXTW|UXTW {#0 | #1}} ] LDRH Wt, [base, Xm {,LSL|SXTX {#0 | #1}} ]
### (5) LDRSH 符号付きハーフワード(16ビット) → 32ビット
LDRSH Wt, [base], #simm9 // ポストインデックス LDRSH Wt, [base, #simm9]! // プレインデックス LDURSH Wt, [base, #simm9] LDRSH Wt, [base {,#uimm12} ] // 符号なしオフセット(2の倍数) [0 - 8190] LDRSH Wt, [base, Wm {,SXTW|UXTW {#0 | #1}} ] LDRSH Wt, [base, Xm {,LSL|SXTX {#0 | #1}} ]
### (6) LDRSH 符号付きハーフワード(16ビット) → 64ビット
LDRSH Xt, [base], #simm9 // ポストインデックス LDRSH Xt, [base, #simm9]! // プレインデックス LDURSH Xt, [base, #simm9] LDRSH Xt, [base {,#uimm12} ] // 符号なしオフセット(2の倍数) [0 - 8190] LDRSH Xt, [base, Wm {,SXTW|UXTW {#0 | #1}} ] LDRSH Xt, [base, Xm {,LSL|SXTX {#0 | #1}} ]
### (7)LDRB バイト(8ビット) → 32ビット
LDRB Wt, [base], #simm9 // ポストインデックス LDRB Wt, [base, #simm9]! // プレインデックス LDURB Wt, [base, #simm9] LDRB Wt, [base {,#uimm12} ] // 符号なしオフセット [0 - 4095] LDRB Wt, [base, Wm {,SXTW|UXTW {#0}} ] LDRB Wt, [base, Xm {,LSL|SXTX {#0}} ]
### (8)LDRSB 符号付きバイト(8ビット) → 32ビット
LDRSB Wt, [base], #simm9 // ポストインデックス LDRSB Wt, [base, #simm9]! // プレインデックス LDURSB Wt, [base, #simm9] LDRSB Wt, [base {,#uimm12} ] // 符号なしオフセット [0 - 4095] LDRSB Wt, [base, Wm {,SXTW|UXTW {#0}} ] LDRSB Wt, [base, Xm {,LSL|SXTX {#0}} ]
### (9)LDRSB 符号付きバイト(8ビット) → 64ビット
LDRSB Xt, [base], #simm9 // ポストインデックス LDRSB Xt, [base, #simm9]! // プレインデックス LDURSB Xt, [base,#simm9] LDRSB Xt, [base {,#uimm12} ] // 符号なしオフセット [0 - 4095] LDRSB Xt, [base, Wm {,SXTW|UXTW {#0}} ] LDRSB Xt, [base, Xm {,LSL|SXTX {#0}} ]
### PC相対リテラルアドレッシング
ラベルの位置にあるデータをレジスタに読み込む
LDR Xt, label LDRSW Xt, label LDR Wt, label
[Arm64] レジスタ
31本の汎用レジスタ、スタックポインタ、ゼロレジスタ、プログラムカウンタ、フラグレジスタ、32本のVectorレジスタ、浮動小数点を制御するFPCRレジスタ、FPSRレジスタを持っている
### 汎用レジスタ
x0からx30となっている
wで指定すると32ビットレジスタとして使用できる
### スタックポインタ、ゼロレジスタ、プログラムカウンタ
スタックトップのアドレスを保持するスタックポインタ、常にビットが0になっているゼロレジスタ、実行中のメモリアドレスを保持するプログラムカウンタがある
### NZCV レジスタ (フラグレジスタ)
演算命令の結果を保持するフラグはNZCVレジスタに保持される
条件分岐命令は、レジスタの各ビットの状態で分岐するかしないかを決定する
EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL
### Vectorレジスタ
複数のベクトル要素を同時演算するためにある 0~31の値
### FPCRレジスタ、FPSRレジスタ
レジスタとはマイクロプロセッサ(MPU/CPU)内部にある演算や実行状態の保持に用いる記憶素子
Arm64アセンブラの実行
hello.s
.text .global _start _start: mov x2, #13 // x2 length adr x1, msg // x1 string address mov x0, #1 // x0 stdout mov x8, #64 svc #0 // sys_write mov x0, xzr mov x8, #93 svc #0 // exit msg: .asciz "hello, world\n"
asでアセンブルしてオブジェクトファイルを作り、リンカを使って実行ファイルを作成する
$ ld -o hello hello.o
$ ./hello
hello, world
ファイルサイズ
$ ls -l hello*
-rwxrwxr-x 1 vagrant vagrant 936 Jan 19 18:14 hello
-rw-rw-r– 1 vagrant vagrant 816 Jan 19 18:14 hello.o
-rw-rw-r– 1 vagrant vagrant 386 Jan 19 17:48 hello.s
シンボル情報の表示
$ nm hello
00000000004100a6 T __bss_end__
00000000004100a6 T _bss_end__
00000000004100a6 T __bss_start
00000000004100a6 T __bss_start__
00000000004100a6 T _edata
00000000004100a8 T __end__
00000000004100a8 T _end
0000000000400098 t msg
0000000000400078 T _start
objdumpによる逆アセンブル
$ objdump -d hello
hello: file format elf64-littleaarch64
Disassembly of section .text:
0000000000400078 <_start>:
400078: d28001a2 mov x2, #0xd // #13
40007c: 100000e1 adr x1, 400098
400080: d2800020 mov x0, #0x1 // #1
400084: d2800808 mov x8, #0x40 // #64
400088: d4000001 svc #0x0
40008c: aa1f03e0 mov x0, xzr
400090: d2800ba8 mov x8, #0x5d // #93
400094: d4000001 svc #0x0
0000000000400098
400098: 6c6c6568 .word 0x6c6c6568
40009c: 77202c6f .word 0x77202c6f
4000a0: 646c726f .word 0x646c726f
4000a4: Address 0x00000000004000a4 is out of bounds.
16進数、32ビットなどで出力も可
$ strings hello
hello, world
hello.o
__bss_start__
__bss_end__
__bss_start
__end__
_edata
_end
.symtab
.strtab
.shstrtab
.text
includeした場合
.include "stdio.s" .text .global _start _start: adr x0, msg bl OutAsciiZ bl Exit msg: .asciz "Hello Hpscript\n"
$ as -o sample.o sample.s
$ ld -o sample sample.o
$ ./sample