A64命令とレジスタ・メモリ操作
実際の命令符号を取り出し、加算・保存・比較・分岐による状態変化を求めます。
説明
CPUはメモリ上の命令をプログラムカウンタPCで指定し、解読した操作をレジスタやメモリへ適用します。ArmのA64命令は1命令32ビットです。w0などのWレジスタは32ビットの値を扱い、spはスタック上の位置を表します。比較命令は条件フラグを更新し、後の分岐がその値を使います。
5と3を加えて8をメモリへ保存し、読み戻した値を8と比較する短いプログラムを使います。アセンブルと逆アセンブルで確認した命令語について、ビット欄から番号と即値を取り出し、Pythonで命令規則を計算します。命令アドレスは先頭0x1000、初期spは0x2000と置き、処理前後の状態を同じ履歴に残します。
1命令語とメモリ上の並び
32ビットの命令語をリトルエンディアンの4バイトへ変換します。先頭はスタック16バイトの確保です。
words = [0xd10043ff,0x528000a0,0x52800061,0xb010002,0xb90003e2,0xb94003e3,0x7100207f,0x54000061,0x52800000,0x14000002,0x52800020,0x910043ff,0xd65f03c0]A64の命令語; names = ['sub sp,sp,#16', 'mov w0,#5', 'mov w1,#3', 'add w2,w0,w1', 'str w2,[sp]', 'ldr w3,[sp]', 'cmp w3,#8', 'b.ne mismatch', 'mov w0,#0', 'b done', 'mov w0,#1', 'add sp,sp,#16', 'ret']逆アセンブルとの対応; base = 0x1000教材で使う先頭アドレス; print('\n'.join('%04x %s %s' % (base+4*i,word.to_bytes(4,'little').hex(' '),names[i]) for i,word in enumerate(words)))アドレス・命令バイト・命令名たとえばmov w0,#5の命令語0x528000a0は、メモリではa0 00 80 52です。命令語の数値表記とアドレス順のバイト表示は逆向きになります。隣の命令アドレスとの差は4バイトです。
2スタック領域を確保する
最初のsub sp,sp,#16はspから16を引きます。この命令の即値欄はビット21〜10で、ここでは左シフトなしです。
pc = base; sp = 0x2000; registers = [0]*4初期PC・SP・レジスタ; immediate = (words[0] >> 10)&0xfff12ビットの即値を抽出; sp -= immediate; pc += 4SPと次のPC; immediate,hex(sp),hex(pc)即値と更新後アドレスspは0x1ff0、PCは0x1004です。メモリの領域を使うための位置を変更しただけで、16バイトに特定の値を書いたわけではありません。レジスタの初期0は今回の計算モデルの設定です。
3即値をレジスタへ入れる
二つのmovはMOVZの別名です。ビット4〜0が書込先、20〜5が16ビット即値で、今回のシフトは0です。
decoded = [(word & 31,(word >> 5)&0xffff) for word in words[1:3]]書込先番号と即値; registers[decoded[0][0]] = decoded[0][1]; registers[decoded[1][0]] = decoded[1][1]二つのレジスタへ設定; pc += 82命令分進む; decoded,registers,hex(pc)解読結果と状態w0=5、w1=3、PC=0x100cです。即値は命令の中に埋め込まれた値で、別のメモリアドレスから読み込んだ値ではありません。
4加算の三つのレジスタ番号
add w2,w0,w1では、Rdがビット4〜0、Rnが9〜5、Rmが20〜16です。二つの読出し元を加え、32ビットの書込先へ入れます。
word = words[3]加算命令語; rd,rn,rm = word&31,(word>>5)&31,(word>>16)&31書込先・読出元1・読出元2; registers[rd] = (registers[rn]+registers[rm])&0xffffffff32ビット加算結果; pc += 4次命令へ; (rd,rn,rm),registers,hex(pc)解読と状態番号は(2,0,1)、結果w2=8です。通常のaddは条件フラグを更新しません。フラグを更新するaddsとは命令が異なります。32ビットのWへの書込みは対応するXレジスタの上位32ビットを0にします。
5レジスタからメモリへ保存する
str w2,[sp]はw2の4バイトをspが指す場所へ保存します。今回のオフセットは0です。
stored = registers[2].to_bytes(4,'little')32ビット値を4バイトへ; memory = {sp+i:byte for i,byte in enumerate(stored)}連続するアドレスへ保存; pc += 4次命令へ; [(hex(address),f'{byte:02x}') for address,byte in memory.items()],hex(pc)メモリ内容とPC0x1ff0から08 00 00 00が入ります。レジスタの値8と、それを保存したメモリアドレス0x1ff0は別です。アドレス・データ・読書きの指定がメモリアクセスの要素になります。
6メモリから別のレジスタへ読む
ldr w3,[sp]で同じ4バイトを読みます。レジスタw2の直接コピーではなく、spの指すメモリが読出し元です。
registers[3] = int.from_bytes(bytes(memory[sp+i] for i in range(4)),'little')4バイトを32ビット値として読む; pc += 4次命令へ; registers,hex(pc)レジスタとPCw3=8、PC=0x1018になります。保存後にメモリが変わっていれば、ロード結果も変わります。今回の記録では保存した4バイトがそのまま残っています。
7比較でNZCVを更新する
cmp w3,#8は引算結果を保存せず、N=負、Z=ゼロ、C=借りなし、V=符号付きオーバーフローの条件を更新します。
left,right = registers[3],8比較する二値; result = (left-right)&0xffffffff32ビット減算結果; flags = {'N':(result>>31)&1,'Z':int(result == 0),'C':int(left >= right),'V':int(bool(((left^right)&(left^result))&0x80000000))}条件フラグを計算; pc += 4分岐命令へ; result,flags,hex(pc)結果とNZCV8−8なので結果0、NZCVは0・1・1・0です。w3は8のままで、比較結果0がw3へ上書きされるわけではありません。Cは減算時には借りがなければ1です。
8分岐先と次のPC
b.neはZ=0なら分岐します。命令のimm19欄を符号付きとして読み、4倍して分岐命令自身のPCへ足します。
branch_pc = pc分岐命令のアドレス; encoded = (words[7]>>5)&0x7ffff19ビット変位; displacement = (encoded-(1<<19) if encoded&(1<<18) else encoded)*4符号拡張と4倍; target = branch_pc+displacement相対分岐先; pc = target if flags['Z'] == 0 else branch_pc+4Zを条件に次PCを決める; displacement,hex(target),hex(pc)変位・行き先・選んだPC変位12バイト、分岐先0x1028です。今回はZ=1なので分岐せず、PCは0x1020へ進みます。条件成立時の行き先と、実際に選ばれた次のPCを分けて確認します。
9比較値が変わると経路も変わる
w3が9なら8との差は1でZ=0です。二つの候補値について分岐条件と戻り値の経路を比較します。
[(value,int(value == 8),hex(target if value != 8 else branch_pc+4),1 if value != 8 else 0) for value in [8,9]]比較値・Z・次PC・戻り値8ならZ=1でmov w0,#0の経路、9ならZ=0でmismatchのmov w0,#1へ進みます。どちらも最後にspへ16を戻し、retで呼出し元へ戻る構成です。PC・データレジスタ・メモリ・フラグがそれぞれ異なる役割を持っています。
まとめ
A64の命令語は32ビットで、ビット欄に即値やレジスタ番号が入ります。
レジスタ内の値とメモリアドレスを分け、ロード・ストアの4バイトを照合します。
比較がNZCVを更新し、条件分岐がフラグと相対変位から次のPCを決めます。
操作履歴と解説を振り返れます。
Python 3
words = [0xd10043ff,0x528000a0,0x52800061,0xb010002,0xb90003e2,0xb94003e3,0x7100207f,0x54000061,0x52800000,0x14000002,0x52800020,0x910043ff,0xd65f03c0]; names = ['sub sp,sp,#16', 'mov w0,#5', 'mov w1,#3', 'add w2,w0,w1', 'str w2,[sp]', 'ldr w3,[sp]', 'cmp w3,#8', 'b.ne mismatch', 'mov w0,#0', 'b done', 'mov w0,#1', 'add sp,sp,#16', 'ret']; base = 0x1000; print('\n'.join('%04x %s %s' % (base+4*i,word.to_bytes(4,'little').hex(' '),names[i]) for i,word in enumerate(words)))1000 ff 43 00 d1 sub sp,sp,#16 1004 a0 00 80 52 mov w0,#5 1008 61 00 80 52 mov w1,#3 100c 02 00 01 0b add w2,w0,w1 1010 e2 03 00 b9 str w2,[sp] 1014 e3 03 40 b9 ldr w3,[sp] 1018 7f 20 00 71 cmp w3,#8 101c 61 00 00 54 b.ne mismatch 1020 00 00 80 52 mov w0,#0 1024 02 00 00 14 b done 1028 20 00 80 52 mov w0,#1 102c ff 43 00 91 add sp,sp,#16 1030 c0 03 5f d6 ret
pc = base; sp = 0x2000; registers = [0]*4; immediate = (words[0] >> 10)&0xfff; sp -= immediate; pc += 4; immediate,hex(sp),hex(pc)(16, '0x1ff0', '0x1004')
decoded = [(word & 31,(word >> 5)&0xffff) for word in words[1:3]]; registers[decoded[0][0]] = decoded[0][1]; registers[decoded[1][0]] = decoded[1][1]; pc += 8; decoded,registers,hex(pc)([(0, 5), (1, 3)], [5, 3, 0, 0], '0x100c')
word = words[3]; rd,rn,rm = word&31,(word>>5)&31,(word>>16)&31; registers[rd] = (registers[rn]+registers[rm])&0xffffffff; pc += 4; (rd,rn,rm),registers,hex(pc)((2, 0, 1), [5, 3, 8, 0], '0x1010')
stored = registers[2].to_bytes(4,'little'); memory = {sp+i:byte for i,byte in enumerate(stored)}; pc += 4; [(hex(address),f'{byte:02x}') for address,byte in memory.items()],hex(pc)([('0x1ff0', '08'), ('0x1ff1', '00'), ('0x1ff2', '00'), ('0x1ff3', '00')], '0x1014')registers[3] = int.from_bytes(bytes(memory[sp+i] for i in range(4)),'little'); pc += 4; registers,hex(pc)([5, 3, 8, 8], '0x1018')
left,right = registers[3],8; result = (left-right)&0xffffffff; flags = {'N':(result>>31)&1,'Z':int(result == 0),'C':int(left >= right),'V':int(bool(((left^right)&(left^result))&0x80000000))}; pc += 4; result,flags,hex(pc)(0, {'N': 0, 'Z': 1, 'C': 1, 'V': 0}, '0x101c')branch_pc = pc; encoded = (words[7]>>5)&0x7ffff; displacement = (encoded-(1<<19) if encoded&(1<<18) else encoded)*4; target = branch_pc+displacement; pc = target if flags['Z'] == 0 else branch_pc+4; displacement,hex(target),hex(pc)(12, '0x1028', '0x1020')
[(value,int(value == 8),hex(target if value != 8 else branch_pc+4),1 if value != 8 else 0) for value in [8,9]][(8, 1, '0x1020', 0), (9, 0, '0x1028', 1)]
演習完了