現在のブログ
ゲーム開発ブログ (2025年~) Gamedev Blog (2025~)
レガシーブログ
テクノロジーブログ (2018~2024年) リリースノート (2023~2025年) MeatBSD (2024年)
【Assembly Language】x86-64 vs AArch64 vs RISC-V vs Power ISA
Every time I write about assembly language, I deliberately choose x86-64 assembly.
Not because it's the easiest, but because it's what most people actually have on their machines.
In other words, it's the most practical for the largest number of readers.
Recently, however, AArch64 has been gaining massive popularity.
This is especially true among Mac users, Raspberry Pi users, and people using Copilot+ PCs or Chromebooks (both AArch64-based).
If you're making games for the Nintendo Switch or the upcoming Switch 2, knowing AArch64 assembly is overwhelmingly useful.
On the other hand, for PlayStation 5 developers, x86-64 assembly remains far more relevant.
In addition, RISC-V and, more recently, Power ISA are rapidly spreading in the hobbyist community as fully open instruction set architectures.
This article explains the differences between these four ISAs.
Current State of Major ISAs
Historically there have been countless ISAs.
The 1980s–1990s were truly the "Wild West" of instruction sets, and from the 2000s to 2010s the market consolidated around Intel's x86 and ARM.
But in the 2020s, competition between ISAs seems to be heating up again.
The current mainstream ISAs are these three:
- x86-64
- AArch64
- RISC-V
Additionally, Power ISA became completely open in 2019 and, while still extremely niche, is slowly being adopted in projects where people build their own Linux-based SoCs or single-board computers.
At the moment, the only realistic ways to try it are QEMU + cross-compilation (or if you still have a working Power Mac G5 that hasn't caught fire yet, or if you maintain AIX servers at IBM).
I considered including MIPS64, but nowadays it only lives on in routers and is essentially on life support even there.
LoongArch (the spiritual successor to MIPS64) exists, but outside China it's extremely hard to get hold of.
Why Learn Multiple ISAs?
In the 64-bit era, differences between ISAs certainly exist, but compared to the 8-bit, 16-bit, and 32-bit eras, they are no longer dramatic.
It's not strictly necessary, but knowing multiple ISAs brings clear advantages:
- Knowing even one makes you stand out as someone who can do it. Knowing several makes you an irreplaceable talent to companies.
- Example: In a studio developing cross-platform games for PS5 and Switch 2, being able to write both x86-64 and AArch64 assembly gives you overwhelmingly higher value than someone who only knows C++.
- If you're working on the next generation of Windows at Microsoft... (well, rumor has it AI is taking over that job soon anyway).
- At a company designing single-board computers, understanding both AArch64 and RISC-V puts you far ahead of people who only know one.
Of course, learning ultra-classic ISAs like 6502, Z80, SuperH, or 68k and writing your own 8-bit/16-bit games is also a lot of fun.
Those feel completely different from modern assembly, though (I might cover them in a separate article someday).
Below is a simple "Hello, World" program written for all four ISAs.
I usually write for FreeBSD, but for consistency this time everything targets Linux.
x86-64
section .rodata
msg db "Hello, World!", 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
Hello, World!
注意:アセンブラ言語によって文法が異成ります。
- NASM(ネットワイド・アセンブラー)→ Intel構文
- FASM(フラット・アセンブラー)→ Intel構文(但し細部が違う)
- GAS(GNUアセンブラー)→ AT&T構文
- MASM(マイクロソフト・アセンブラー)→ Microsoft独自構文
チームで作業する場合は、必ず1つのアセンブラーを決めて統一しましょう。
他のアーキテクチャでは基本的にGAS中心なので、此の問題はほぼ発生しません。
AArch64
.section .rodata
msg:
.asciz "Hello, World!\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
Hello, World!
RISC-V
.data
msg:
.asciz "Hello, World!"
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
Hello, World!
Power ISA
.section .rodata
msg:
.asciz "Hello, World!\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
Unfortunately I couldn't get the Power ISA version to run.
Something is still missing...
As you can see, all four versions are extremely similar.
Only Power ISA needs one extra cycle to properly restore the TOC (table-of-contents) pointer — a feature that made Power powerful in the 1990s and still remains today.
Other than that, the flow is basically identical:
- Load the
sys_writesyscall number into a register - Load
fd = 1(stdout) - Load the address of the message string
- Load the string length
- Issue the
syscall - Load the
sys_exitsyscall number - Load exit code 0 and issue the
syscall
Also note that the three RISC architectures use .asciz (null-terminated ASCII string), whereas x86-64 manually adds , 0 at the end to null-terminate the string.
This is exactly why people say: "Learn assembly for one ISA and you'll pick up any other ISA almost instantly".
The feeling is very close to "If you know C, you'll learn C++, C#, Java, Go, Zig, etc. in no time" or "If you know Python, shell scripting and Ruby are trivial".
How do you write assembly that works on multiple ISAs?
Short answer: You don't.
Slightly kinder answer: Just use C.
Writing assembly inherently means targeting a specific ISA.
Add to that huge differences between operating systems (Windows vs Unix-like vs bare-metal), and writing portable assembly becomes practically impossible.
If you want bare-metal code, you essentially end up writing your own OS anyway.
That's all