一覧OS-01-1 · CPUと命令実行

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)メモリ内容とPC

0x1ff0から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)レジスタとPC

w3=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)結果とNZCV

8−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

1
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
2
pc = base; sp = 0x2000; registers = [0]*4; immediate = (words[0] >> 10)&0xfff; sp -= immediate; pc += 4; immediate,hex(sp),hex(pc)
(16, '0x1ff0', '0x1004')
3
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')
4
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')
5
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')
6
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')
7
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')
8
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')
9
[(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)]

演習完了