現在のブログ
ゲーム開発ブログ (2025年~) Gamedev Blog (2025~)
レガシーブログ
テクノロジーブログ (2018~2024年) リリースノート (2023~2025年) MeatBSD (2024年)
【アセンブリ言語】x86-64 vs AArch64 vs RISC-V vs Power ISA
アセンブリ言語について投稿する度に、あたしは意図的にx86-64アセンブリを選んでいます。
其れは其れが一番簡単だからではなく、殆どの人がx86-64マシンを持っているからです。
詰り、より多くの人に実践的だからです。
然し最近AArch64がどんどん人気になってきています。
特にMacユーザーやRaspberry Piユーザー、そしてCopilot+PCやChromebook(どちらも AArch64 系)を使っている人達の間でそうです。
更に、Nintendo Switch / Switch 2向けにゲームを作る人はAArch64アセンブリを知っていた方が圧倒的に役に立ちます。
一方、PlayStation 5の開発者にとってはx86-64アセンブリの方が有用です。
加えて、RISC-Vと最近ではPower ISAがオープンなISAとしてホビースト界隈で急速に広がっています。
其処で本記事では、此れらの違いについて解説します。
主な ISA の現状
歴史的には無数のISAが存在しました。
1980~1990年代は正に「ISAの西部開拓時代」で、2000~2010年代にはIntel(x86)とARMに市場が集約されました。
然し2020年代に入り、再びISA間の競争が活発化している様に見えます。
現在の主流は以下の3つです:
- x86-64
- AArch64
- RISC-V
更にPower ISAも2019年に完全オープン化され、極めてニッチながらLinuxベースのSoCやSBCを自作するプロジェクトで徐々に採用され始めています。但し現時点では非常にニッチで、実際に触るにはQEMU+クロスコンパイルが保母唯一の手段です(或いはPower Mac G5が未だ燃えずに残っていれば其れを使えるか、IBMでAIXサーバーを保守している人なら)。
MIPS64も入れようか迷いましたが、今はルーターくらいでしか使われておらず、其処でも保母死に体です。LoongArch(MIPS64の精神的后継)もありますが、中国以外では入手が極めて困難です。
何故複数のISAを学ぶのか
64ビット時代に入ってからのISA間の違いは確かに存在しますが、8ビット・16ビット・32ビット時代に比べれば劇的とは言えません。
必須ではありませんが、複数のISAを知っていると明確なメリットがあります。
- 1つでも知っていれば出来る人として目立ちますが、複数知っていると企業にとって替えのきかない人材に成ります。
- 例:PS5とSwitch 2向けにクロスプラットフォームゲームを作るスタジオで働く場合、x86-64とAArch64の両方のアセンブリ言語が書ければ、C++しか書けない人より圧倒的に価値が高いです。
- Microsoftで次世代Windowsを開発しているなら・・・(まあ最近はAIに置き換えられる方向らしいですが)
- SBC(シングルボードコンピュータ)を開発する企業なら、AArch64とRISC-Vの両方が分かれば、片方しか知らない人より大きくリードできます。
勿論、6502・Z80・SuperH・68kといった超古典ISAを学び、自作8ビット/16ビットゲームを書くのも楽しいです。
但し其れらは現代のアセンブリ言語とはか成り感覚が違います(いずれ別記事で取り上げるかもしん)。
以下に、シンプルな「こんにちは、世界」プログラムを4つのISAで書いてみます。
普段はFreeBSD向けに書いていますが、今回は統一の為Linux向けにします。
x86-64
section .rodata
msg db "こんにちは、世界!", 10, 0
msgLen equ $-msg
section .text
global _start
_start:
mov rax, 1
mov rbx, 1
lea rsi, msg
mov rdx, msgLen
syscall
mov rax, 60
mov rbx, 0
syscall
$ nasm -f elf64 main.s -o main.o
$ ld main.o -o main
$ ./main
こんにちは、世界!
注意:アセンブラ言語によって文法が異成ります。
- NASM(ネットワイド・アセンブラー)→ Intel構文
- FASM(フラット・アセンブラー)→ Intel構文(但し細部が違う)
- GAS(GNUアセンブラー)→ AT&T構文
- MASM(マイクロソフト・アセンブラー)→ Microsoft独自構文
チームで作業する場合は、必ず1つのアセンブラーを決めて統一しましょう。
他のアーキテクチャでは基本的にGAS中心なので、此の問題はほぼ発生しません。
AArch64
.section .rodata
msg:
.asciz "こんにちは、世界!\n"
msgLen = .-msg
.section .text
.global _start
_start:
mov x8, #64
mov x0, #1
adr x1, msg
mov x2, msgLen
svc #0
mov x8, #93
mov x0, #0
svc #0
$ as main.s -o main.o
$ ld main.o -o main
$ ./main
こんにちは、世界!
RISC-V
.data
msg:
.asciz "こんにちは、世界!"
msgLen = .-msg
.text
.global _start
_start:
li a7, 64
li a0, 1
la a1, msg
li a2, msgLen
ecall
li a7, 93
li a0, 0
ecall
$ as main.s -o main.o
$ ld main.o -o main
$ ./main
こんにちは、世界!
Power ISA
.section .rodata
msg:
.asciz "こんにちは、世界!\n"
msgLen = .-msg
.section .text
.globl _start
_start:
ld 2, 24(12)
li 0, 4
li 5, 1
ld 3, msg@toc(2)
li 4, msgLen
sc
li 0, 1
li 3, 0
sc
$ powerpc64-linux-gnu-as main.s -o main.o
$ powerpc64-linux-gnu-ld main.o -o main
$ qemu-ppc64-static ./main
残念ながら此方は動作確認が取れませんでした。
何か足りない様です・・・
然し見ての通り、4つ全てが非常に似ています。
Power ISAだけは正しいTOC(目次ポインタ)を復元するために+1サイクル必要です。
此れが1990年代にPowerを強力にした特徴ですが、今でも残っています。
其れ以外は基本的に同じ流れです:
sys_writeの番号をレジスタにロードfd = 1(stdout)をロード- メッセージ文字列のアドレスをロード
- 文字列の長さをロード
syscall発行sys_exitの番号をロード- 終了コード0をロードして
syscall
又、RISC系の3つでは.asciz(NULL終端ASCII文字列)を使っているのに対し、x86-64では最後に, 0を付けてNULL終端にしている点も注目して下さい。
だからこそ「1つのISAのアセンブリ言語を覚えれば、他のISAも直ぐ覚えられる」と言われるのです。
此れは正に「C言語を覚えればC++、C#、Java、Go、Zig等は直ぐ覚えられる」「Pythonが分かればシェルスクリプトやRubyも直ぐ書ける」に近い感覚です。
マルチISA対応のアセンブリ言語はどう書くのか?
短い答え:書けません。
もう少し優しい短い答え:C言語で書きましょう。
本質的に、アセンブリ言語を書くという事は「特定のISAをターゲットにする」事です。
更にOSに余っても違いが大きく、Windows用とUnix系用、ベアメタル用は全く別物です。
ベアメタルで動かしたいなら、事実上自分でOSを書く事に成ります。
以上