Programming with 64-Bit ARM Assembly Language: Single Board Computer Development for Raspberry Pi and Mobile Devices

5. Thanks for the Memories メモリ


2021.08.12: updated by
ARM プロセッサは load-store architecture を採用している。 これはすなわち、命令が に2分割されていることを意味する。 メモリのアドレッシングは64bit幅 であるが、命令は32bit幅である。 そのため2章「Loading and Adding」で遭遇したのと同じ問題が起きる。 すなわち、32bit幅の命令を用いて64bitのアドレスをレジスタに設定しなくてはいけない。 分割してloadすることも可能であるが、1命令で設定することもできる。

Defining Memory Contents

メモリのload/storeの前に、メモリを定義する必要がある。 GNU Assembler は使用するメモリを定義する directives がいくつかある。
  .byte    1-byte 整数
  .short   2-byte 整数
  .word	   4-byte 整数
  .quad    8-byte 整数
  .octa    16-byte 整数
  .ascii   ""で囲まれた文字列
  .asciz   0で終わるascii文字
  .float   浮動小数点数
  .double  倍精度浮動小数点数
10進数は,0以外からはじまる0-9の文字。 8進数は,0からはじまる0-7の文字。 2進数は 0b or 0B からはじまる0-1の文字。 16進数は 0x or 0X からはじまる0-Fの文字。 浮動小数点数は、0f or 0e からはじまる。
  .fill  repeat, size, value
  (例)
  .fill  10, 4, 0   // 4-byteで表した0を10回繰り返す
  .rept count
  ...
  .endr
.rept と .endr の間のstatementをcount回繰り返す。
  (例)
  .rept 3
  .byte 0, 1, 2
  .endr

  (例:以下の3行と同じ意味)
  .byte 0, 1, 2
  .byte 0, 1, 2
  .byte 0, 1, 2
ascii文字列の中では次の Escape character sequence で文字を指定できる。
  Escape Character Sequence      Description
  \b                             Backspace (8)
  \f                             Form feed (12)
  \n                             New line (10)
  \r                             Return (13)
  \t                             Tab (9)
  \ddd                           8進数によるascii code
  \xdd                           16進数によるascii code
  \\                             文字 '\'
  \"                             double quote  
  \anything-else                 Anything-else

Aligning Data

(例)
.data
  .byte  0x3f
  .align 4
  .word 0x12345678
ARM プロセッサは命令を word aligned に配置しなくてはならない。

Loading a Register with an Address

LDR 命令

LDR命令を、 「アドレスをレジスタにloadする」「そのアドレスからデータをレジスタにloadする」 という2つの異なる操作に用いる。

PC Relative Addressing PC相対アドレッシング

命令中で、PCからのオフセットは、19bitを占める。word単位で指定するのでアドレスとしては21bit指定できることになるので +/- 1M-byte のオフセットを指定できる。 次のように書くと任意の64bitアドレスをレジスタにloadできる。
  LDR  X1, =0x1234ABCD1234ABCD
  上のように書くと、以下のようrに展開する。
  ldr  x1,  #8
  .quad  0x1234abcd1234abcd
アセンブラはPC相対で指定されるメモリ番地を .textセクションの最後に配置する。 .dataセクションではないので上書きは不可能である。 PC相対でデータ領域を直接指すことができないのは ARM命令セットに固有の理由がある。
  1. 1MBのオフセットは大きいように見えるが、現代のコンピュータでは、アドレスだけが集まった場所は、メモリの断片となる。 1MB wordではなく1MBオブジェクトをアクセスできる。
  2. 定義したラベルはすべて、オブジェクトファイルのsymbol tableに保持される。 ユーザがコンパイルし直さなくても、linker, loader, OSがメモリアドレスを変更することが簡単にできる。
  3. これらの変数を、プログラムを変更することなく、大域変数にできる。

Loading Data from Memory

  LDR{type}  Xt, [Xa]
Type    Meaning
-----------------
B       Unsigned byte
SB      Signed byte
H       Unsigned haldword (16bits)
SH      Signed haldword (16bits)
SW      Signed word
符号付きの場合は、データをloadするときは符号拡張を行う。 上の表に 'W' がないのは、レジスタとして Wn を使えばよいからである。
(例)
  LDR  X1, =mynumber
  LDR  X2, [X1]     // [Xn] indirect memory access
.data
mynumber:   .QUAD    0x123456789ABCDEF0

Indexing Through Memorhy

  A[5] =5
  X = A[3]
  FOR i=1 to 10
    A[i] = I ** 3
arr1:   .FILL  10, 4, 0

	LDR  X1, =arr1

	LDR  W2, [X1]    // load index 0 (= element 1st)
	LDR  W2, [X1, #(2 * 4)]  // load index 2 (= element 3rd)
        // 上の1行と下の2行は同じ結果となる
	MOV  X3, #(2 * 4)
	LDR  W2, [X1, X3]  // X1からオフセットX3の位置にあるメモリのデータを32bit単位でW2にコピーする。
Wn はレジスタに32bitでアクセスし、Xnは64bitでアクセスする。 アドレスはいつも64bitなので、レジスタとしてXnを使う必要がある。 オフセットでレジスタを使う場合は Operand2 も指定することができる。
    MOV  X3, #2
    LDR  W2, [X1, X3, LSL #2]   // X3の値(2)に4を乗算した結果をオフセットとして使う

Write Back

アドレスが計算されると、レジスタにloadした後は結果が捨てられる。 しかし、loopの場合は、計算されたアドレスを残しておけると、 Add命令を別に実行する必要がなくなるので便利である。 このために '!' を使う。
    LDR  W2,  [X1,  #(2 * 4)]!    // X1の値が更新される

Post-Indexed Address

[]の外に post-index addressingを書く。
    LDR  X1, [X2], #2    // X1 := [X2],  then  X2 := X2+2

Converting to Upper-Case

.global  _start
_start:
        LDR     X4, =instr
	LDR     X3, =outstr
loop:
	LDRB    W5, [X4], #1
	
	CMP     W5, #'z'    // if w5 >'z' goto cont
	B.GT    cont

	CMP     W5, #'a'    // if w5 < 'a' goto cont
	B.LT    cont

	SUB     W5, W5, #('a'-'A')

cont:
	STRB    W5, [X3], #1
	CMP     W5, #0
	B.NE    loop

	// 表示
	MOV     X0, #1        // 1 = stdout
	LDR     X1, =outstr   // string to print
	SUB     X2, X3, X1    // X2 = X3 - X1 = stringの長さ
	MOV	X8, #64       // Linux output string API
	SVC     0

	MOV     X0, #0        // Return cord = 1
	MOV     X8, #93       // Linux termination API
	SVC     0

.data
instr:    .asciz  "This is our Test String that we will convert.\n"
outstr    .fill   255, 1, 0

Storing a Register

STR (Store Register)命令は LDR命令と鏡像関係にある。 LDRで説明したアドレッシングはすべてSTR命令でも利用できる。

Double Registers

LDR 命令や STR 命令の double word 版命令がある。 LDP 命令はレジスタのペアをパラメータとして指定し、メモリから128 bit をロードする。 STP 命令はレジスタのペアをパラメータとして指定し、メモリへ128 bit をストアする。
    LDR    X1, =myoctaword
    LDP    X2, X3, [X1]
    STP    X2, X3, [X1]
.data
myoctaword:  .OCTA 0x12345678123456781234567812345678    // 4 * 32 = 128 (bit)


http://karel.tsuda.ac.jp/