目的
x86 CPUのレジスタは16ビット→32ビット→64ビットと拡張されてきました。
例えば16ビットのレジスタAX
は32ビットになるとEAX
,64ビットではRAX
と名前が変わります。
教材等では64ビットのRAX
はEXA
とすると下32ビット、AX
とすると下16ビットのレジスタとして単独で使用できると書かれているのを良く目にします。
C言語をコンパイルすると、64ビットの環境でもEAX
レジスタが使われることもありますがAX
が使われているのは見たことがありません。
私の他の記事では良く16ビットのアセンブリを書いており、見慣れているAX
レジスタですが、64ビットの環境で果たしてまだ使えるのかどうかを検証したいと思います。
やり方
RAX
に0x1A2B3C4D5E6F7A8B
を代入したあと、AX
に0x1234
を代入することで7A8B
→1234
へと変化するかを観察します。
レジスタの値を画面表示するには一工夫が必要です。4ビットずつASCIIコードへ変換します。
10以上は英字になるため処理を分けます。
0〜9 : 0x0〜0x9(0〜15)
10〜15 : 0xA〜0xF(0〜15)
数字を対応するASCIIコードへ変換するには'0'(0x30)を足します。
数字を対応するASCIIコードへ変換するには'A'-10を足します。
'A' の ASCII コードは 65(0x41)
10 に 'A'-10 = 65-10 = 55 を足すと 10→65 → 'A' になる
同様に 11→66 → 'B'、12→67 → 'C' … 15→70 → 'F'
実装
test@test-ThinkPad-X280:~/test/ax$ nasm -felf64 test.asm -o test.o
test@test-ThinkPad-X280:~/test/ax$ ld test.o -o test
test@test-ThinkPad-X280:~/test/ax$ ./test
RAX=0x1A2B3C4D5E6F1234
test@test-ThinkPad-X280:~/test/ax$
section .data
buf db 'RAX=0x0000000000000000', 10
len equ $ - buf
section .text
global _start
_start:
mov rax, 0x1A2B3C4D5E6F7A8B ; RAX初期値
mov ax, 0x1234 ; AXを通じて下位4ビットのみ変更
; 数字をASCIIコードへ変換してbufへ入れて行く
lea rsi, [buf+6]; 「RAX=0x」の後ろを指す
mov rcx, 16 ; 16個の数字を表示する
mov rbx, rax
.convert_loop:
mov rax, rbx
and rax, 0xF ; 下位4ビット取り出し
cmp rax, 10 ; 10未満か
jb .digit
add al, 'A'-10 ; 16進数の10〜15をそのまま ASCII の 'A'〜'F' に変換
jmp .store
.digit: ; 10未満は数字を出力。
add al, '0' ; '0'を足してASCIIへ変換
.store:
mov [rsi+rcx-1], al
shr rbx, 4 ; ビットを右へ4ビットずらす(処理中4ビットの左側4ビットが一番右に来る)
loop .convert_loop
; write(1, buf, len)
mov rax, 1 ; sys_write
mov rdi, 1 ; fd=1 (stdout)
mov rsi, buf
mov rdx, len
syscall
; exit(0)
mov rax, 60 ; sys_exit
xor rdi, rdi
syscall