代入とassembler

c

#include <stdio.h>
// mainブロック
int main(void) {

    int age;
    age = 20;
    printf("私は%d歳です\n", age);
    return 0;
}

assembler

|main|  PROC
|$LN3|
        stp         fp,lr,[sp,#-0x20]!
        mov         fp,sp
        mov         w8,#0x14
        str         w8,[sp,#0x10]
        ldr         w1,[sp,#0x10]
        adrp        x8,|$SG4981|
        add         x0,x8,|$SG4981|
        bl          printf
        mov         w0,#0
        ldp         fp,lr,[sp],#0x20
        ret

str
ストア命令 (STR) はレジスタに格納されている値をメモリに書き込み

ageをdoubleにするとこうなる
ラベルでldrしている

|$LN4|
        stp         fp,lr,[sp,#-0x20]!
        mov         fp,sp
        ldr         d16,|$LN3@main|
        str         d16,[sp,#0x10]
        ldr         x1,[sp,#0x10]
        adrp        x8,|$SG4981|
        add         x0,x8,|$SG4981|
        bl          printf
        mov         w0,#0
        ldp         fp,lr,[sp],#0x20
        ret
        nop
|$LN3@main|
        DCFD         20.5

DCFD and DCFDU: The DCFD directive allocates memory for word-aligned double-precision floating-point numbers, and defines the initial runtime contents of the memory. DCFDU is the same, except that the memory alignment is arbitrary.
https://developer.arm.com/documentation/dui0801/l/Directives-Reference

型によってassemblerのオペランドが異なっていることがわかる

printfとassembler

c

#include <stdio.h>
int main(void) {

    printf("テキスト1");
    printf("テキスト2");
    return 0;
}

assembler

|main|  PROC
|$LN3|
        stp         fp,lr,[sp,#-0x10]!
        mov         fp,sp
        adrp        x8,|$SG4980|
        add         x0,x8,|$SG4980|
        bl          printf
        adrp        x8,|$SG4981|
        add         x0,x8,|$SG4981|
        bl          printf
        mov         w0,#0
        ldp         fp,lr,[sp],#0x10
        ret

STP
Store Pair of Registers calculates an address from a base register value and an immediate offset, and stores two 32-bit words or two 64-bit doublewords to the calculated address, from two registers. For information about memory accesses, see Load/Store addressing modes.
命令は 2つのレジスタ (Xt1、Xt2) の内容を、メモリアドレスを保持するレジスタ (Xn) にオフセットの値を加えたアドレスを先頭とするメモリに書き込み、レジスタをスタックに退避する

adrp
プログラムカウンタ (PC) に ±4GB の範囲のオフセットを加えたアドレスを 4KBを単位としたページアドレスに変換して、指定したレジスタ(Xd) に書き込みます。 ADRP命令のオフセットの値には immHi の19ビットと immL の2ビットを加えた21ビットの範囲の整数を 4096倍(12bit) した整数が指定できるため、PCの値±4GB の範囲の 4KB を単位としたページアドレスを指定できます。

bl
ンク付分岐命令の BL は、プログラムカウンタの値にオフセットを加えた メモリアドレスに分岐(ジャンプ)します。 分岐先の命令に付けたラベルを分岐先として指定しますが、 アセンブラが「BL」命令のアドレスとラベルのアドレスから、 プログラムカウンタ相対オフセットを計算します。 オフセットの値は内部的には26ビットの符号付整数ですが、 プログラムカウンタの値は常に4の倍数であることを利用するため、 分岐先は ±128MB の範囲が可能

lr リンクレジスタ
L x30レジスタ 関数コールした時の戻り番地を記憶
fp フレームポインタ
L x29レジスター
プログラムカウンター(PC)
L 次に実行される命令のアドレスを保持するレジスタです
スタックポインター(SP)
L スタックのトップを指すレジスタです
ステータスレジスター(NZCV)
L 直前の演算の結果に関するフラグを保持するレジスタです

コンパイラ基礎

スタックは木構造の解析に優れている

コンパイラの論理構造
– 字句解析
– 構文解析
– 中間語作成
– 最適化
– コード生成
↑ 変数名表、定数表など各種情報の表を使用する

中間語列は
(*, e3, 2.56, T0)
(/, abc, e3, T1)
などアセンブラを仮定したもので生成する

最適化
– 無駄なものを省いたり、実行時に効率の良いものにする
– コンパイラの物理構造はコンパイラによって異なる

x86アセンブリ

アセンブリを学ぶメリット

CPU を支配できる.
コンパイラより高速なコードを書ける
文法が簡単である.
バイナリ (実行できるプログラム) が小さい.
まったく新しい OS を作るには必須の知識である.
ウイルスに負けない体力を養える.
コンパイラやインタプリタの作者になれる.
Linux カーネルの機能を理解できる.
C のポインタが簡単に理解できる.

OS(ブートローダー、カーネル、デーモン、シェル、デスクトップマネージャ、アプリケーション)、カーネル、インタプリタ、コンパイラ周りを理解するには必須のスキルだということがわかる。

hello.asm

  ;------------------------------------
  ; hello.asm
  ;------------------------------------
  section .text
  global _start

  msg             db   'hello, world', 0x0A
  msglen          equ  $ - msg

  _start:
                  mov    ecx, msg       ; 文字列の場所を指定
                  mov    edx, msglen    ; 文字列の長さを設定
                  mov    eax, 4         ; 出力のシステムコール
                  mov    ebx, 1         ; 標準出力を指定
                  int    0x80           ; システムコール実行
                  mov    eax, 1         ; 終了のシステムコール
                  mov    ebx, 0         ; 正常終了の 0 に設定
                  int    0x80           ;  システムコール実行
  ;------------------------------------

プログラムを終了するサブルーチン

Exit:
               mov    eax, 1         ; sys_exit
               mov    ebx, 0         ; exit with code 0
               int    0x80

異常終了

;------------------------------------
; exit with ebx
ExitN:
               mov    ebx, eax       ; exit with code ebx
               mov    eax, 1         ; sys_exit
               int    0x80

writeのシステムコール

;------------------------------------
; print string to stdout
; eax : top address
; edx : no of put char
OutString:
               pusha
               mov    ecx, eax
               mov    eax, SYS_write
               mov    ebx, 1         ; to stdout
               int    0x80
               popa
               ret
;------------------------------------
; get length of asciiz string
; eax : top address
; eax : return length
StrLen:
               push   ecx
               push   edi
               mov    edi, eax
               push   eax
               xor    eax, eax
               mov    ecx, 0xFFFF  ; no more than 65k chars.
         repne scasb
               pop    ecx
               sub    edi, ecx
               mov    eax, edi
               pop    edi
               pop    ecx
               ret

;------------------------------------
; print asciiz string
; eax : pointer to string 
OutAsciiZ:
               push   edx
               push   eax
               call   StrLen
               mov    edx, eax
               pop    eax
               call   OutString
               pop    edx
               ret

ARMアセンブリ

ARM は RISC でありながら、個々の命令が複雑な機能を持っています。特にほとんどの命令が条件に従って実行されたり、されなかったりする条件実行の機能を持っている
スタックが使いやすく、比較的高機能な命令が多いため、アセンブラでプログラムしやすいCPUである
ARM系のCPUの場合、システムコールの方法やスタックの使い方を規定した Application Binary Interface (ABI) に 2種類ある

LR(リンクレジスタ)は、分岐命令(サブルーチン呼び出し)が行われたときに、サブルーチン終了後に呼び出し元に戻ってこられるよう、現在の PC 値(正確には、現在の PC 値の次の命令のアドレス)を覚えておくためのもの
プログラムの実行とともに「プログラムカウンタ(PC)」が更新されます。プログラムカウンタとは、CPU内にあるアドレス(番地)を収める場所で、そのCPUが次に実行する命令が置かれている番地が書かれている
Pentium系のCPUと比べて命令数は少ないのですが、 条件実行が可能なことや演算命令でフラグレジスタへの反映の有無を指定できることから、 それらの組み合わせでニーモニック(命令の表現)が多い

PowerPCアセンブリ

PowerPCと他のCPU(x86, MIPS, ARM)との最も異なる点は、条件比較命令で変化する条件レジスタが 8 セットあり、比較命令でフラグを設定する条件レジスタを指定することができ、各セット間の論理演算が可能な点と分岐命令の分岐条件に条件レジスタの 8セットのうちの1つを指定できる点
読み書きの対象となるメモリアドレスの指定方法(アドレッシングモード)が単純である一方、ネイティブなニーモニックが複雑(人間向きでない)
MIPS, ARMと同様に命令が32ビット固定長、ARMと同じくMIPSやSHので採用されている遅延スロット(慣性:分岐する場合でも1命令余分に実行)がない、 MIPSと同じくx86やARMが持つようなレジスタをスタック退避する便利な命令がない (MIPSよりは便利)といった、MIPSとARMの中間の性質がある

### 汎用レジスタ
32本の汎用レジスタ(General Purpose Register)があり、演算命令、比較命令、メモリ転送命令など多くの命令のオペランドとして使い、数値、アドレスを格納

x86とは?

x86はインテルがパソコンなど向けに開発製造しているマイクロプロセッサ
8086と共通の命令セットで、Intel Core i3/i5/i7/i9などにも引き継がれている
AMD64に習い64ビットプロセッサをx86-64と呼んでいいる

PowerPCとは?

PowerPCとは、Apple, IBM, Motorolaによって開発されたRISCアーキテクチャ
AppleのMacintoshやIBMのRS/6000などで採用
ゲーム機、組み込みシステム、スーパーコンピュターで使用
現在は新規採用されるケースは少ない

[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"