6502命令セットが最初のアセンブリ言語に適している理由
(nemanjatrifunovic.substack.com)- アセンブリはCPUアーキテクチャと直接結びついているため、最初に学ぶ対象が重要だが、6502は単純な構造と実際のエコシステムを兼ね備えた入門向けプラットフォームである
- レジスタ6個と元来の命令56個だけで、レジスタ、ロード/ストア、論理・算術演算、分岐といった基本概念を素早く身につけられる
- Apple II、Commodore VIC 20、Commodore 64、BBC microなどで使われた実在のCPUであり、エミュレータや再現機、無料資料を活用した実習ベースの学習がしやすい
- Easy 6502はWeb上ですぐにコードを書いて試せるJavaScriptベースのアセンブラとシミュレータを提供しており、短時間で感覚をつかむのに向いている
- Z80、現代RISC、x86-x64も学ぶ価値はあるが、最初の段階では複雑さの低い6502のほうが学習負荷を抑えられる
6502が入門向けに適している理由
- アセンブリプログラミングは高級言語よりもハードウェアとの結びつきが強く、最初に触れるCPUが学習難易度を大きく左右する
- 6502アセンブリは、今日の実務アプリケーションを書くうえで快適な環境とは言えない
- レトロプログラミング自体に関心がないなら、6502に長くとどまる必要はない
- 入門用プラットフォームとしての利点は単純さと実在性にある
- 単純な構造のおかげで基本概念を素早く学べる
- 実機、エミュレータ、書籍やオンライン資料が多く、学習の道筋を立てやすい
-
レジスタと命令数が小さく保たれている
- 6502のレジスタは6個しかない
- アキュムレータ1個
- インデックスレジスタ2個
- ステータスレジスタ
- スタックレジスタ
- プログラムカウンタ
- 6502で唯一の16ビットレジスタはプログラムカウンタである
- レジスタ数の少なさは実際のアプリケーションを書くには制約になるが、レジスタの役割と動作を理解する助けになる
- 元来の6502命令セットには56個の命令しか含まれていない
- この小さな命令集合だけでも主要な命令の種類を学べる
- ロード/ストア
- 論理演算
- 算術演算
- 分岐
- こうした種類は現代のCPUにも何らかの形で残っている
- x86-x64はレジスタ数や命令数を数えること自体が簡単ではなく、ARMのような現代RISC命令セットも小さく単純だとは言いがたい
- 6502のレジスタは6個しかない
実際のエコシステムと他の選択肢
-
古いが今も生きているエコシステム
- 6502は1970年代半ばに、MOS TechnologyがMotorola 6800の廉価で簡略化した版として導入した
- 同じチームがそれ以前にMotorola 6800を開発していた
- その後すぐに人気を集め、多くの著名なマイクロコンピュータで使われた
- Apple II
- Commodore VIC 20
- Commodore 64
- BBC micro
- こうした機種のエミュレータは見つけやすく、一部には現代的な再現ハードウェアもある
- 6502の後継製品は執筆時点でも生産されており、OlimexのNeo6502のような新しい機器も6502系を実行する
- 無料の学習資料はインターネット上に豊富にあり、その多くは6502.orgで見つけられる
- 6502は1970年代半ばに、MOS TechnologyがMotorola 6800の廉価で簡略化した版として導入した
-
すぐに実習できる学習資料
- Easy 6502はNick Morganによる電子書籍で、短時間だけ6502に触れてみたい学習者に適している
- テキストや画像だけでなく、JavaScriptで書かれた6502アセンブラとシミュレータも含んでいる
- Webページ上で簡単な6502アセンブリコードをその場で書ける
- Visual6502.orgはアセンブリ学習ツールというより、6502の単純さを視覚的に示す資料に近い
-
他の入門候補との違い
- 教育用に作られた仮想CPUは、整然として「完璧な」命令セットを持てるかもしれないが、6502ほど豊富な学習資料やコミュニティを備えるのは難しい
- Z80、6809、68000のようなレトロCPUも良い選択肢である
- この3つのCPUは、本格的なプログラミングでは6502より優れていると評価される
- 基本概念の学習では6502の単純さのほうが有利である
- Z80は6502よりレジスタがはるかに多く、代替レジスタセットもあるため、始めやすさでは劣る
- ARM、MIPS、RISC-Vのような現代RISCアーキテクチャは、いずれ本格的なアセンブリプログラマが学ぶべき対象である
- ただし入門用としては理想的ではない
- それらの単純さは命令セットそのものより、チップ内部実装の側面に近い
- 現代のマイクロプロセッサの大半は高級言語でプログラミングされており、直接アセンブリ命令を書く体験はCPU設計の優先順位として高くない
- ARM64で64ビット定数をレジスタにロードするには、ビットシフトを含めて4命令必要になることがある
- 6502で定数をレジスタにロードする手順は、それに比べると単純である
- x86-x64は現代のデスクトップやサーバで最も一般的な命令セットと言え、ARMがそれを追い上げている
- 32ビット長の制約を受けず、オペランド数が可変の豊富な命令があるため、ARMよりコーディングしやすい面もある
- それでも初心者には依然として非常に複雑であり、40年の歴史がx86-x64をより単純にしたわけではない
1件のコメント
Hacker Newsのコメント
6502の単純さが68000より優れているという点には同意しにくい
68000はレジスタが多くデータ幅も広いが、構造は均一だ。実質的にはAとDの2種類がD0〜D7、A0〜A7として繰り返される形で、D0でできることはD3でもできる。構造の単純さはプログラミングの単純さとバランスを取るべきで、6502は少し複雑なプログラムを書くだけでも制約に何度も突き当たる
小さなマシンの限界を回避する方法を学ぶ最良の道は、むしろその限界を無視して熟練したソフトウェアエンジニアになることだ。初心者なら、アドレッシングモードが多く、大きな配列を扱いやすく、レジスタや整数幅に余裕がある方から始めるほうがよい
6502はレジスタが6個あるから単純だと主張しているが、6502を1週間も触れば、実際に扱っているのは3つのレジスタだと分かる。だからセマフォCPUというあだ名まである
6502は楽しくてすっきりしているが、現代的なアプローチを望むなら向いていない。90年代風の栄光への道をたどりたいならMIPSから始めるか、あるいはそのままNeonから始めるほうがよい。Z80も、レジスタや複雑さを考えれば、今日出会うものにより近いと言える余地がある
現代のアセンブリ自体が本質的に複雑なわけではない。FASMから始めても難しくはないが、低水準のアプローチはいつもすぐ複雑になる。やがてマクロを作り始め、結局は自分専用の貧弱なCもどきを上に載せることになる
小さなシステムの利点は、シリコンからOS、ソフトウェアまで全体を完全に理解できる点にある
6809は68Kより単純で6502より強力、しかも直交性が高いが、同じレベルのソフトウェア基盤がない。Motorolaは6809と68Kの命令セットやプログラマ視点のアーキテクチャで、かなり良い方向性を示していたと思う。PDP-11/VAXやNS32Kも同様に直交的だった記憶がある
もっと現代的なプラットフォームを考えるなら、MC68kよりRISC-Vを教える方が強い説得力がある。RISC-Vは基本整数命令セットだけを見れば非常に単純でエレガントでありながら、ARM、Aarch-64、MIPSのような現代アーキテクチャに似ている。32ビットと64ビットの両方があり、公式ドキュメントも利用しやすい
MC68kには、今日ではあまり関係のない癖が多すぎる。利点があるとすれば、複数のハードウェアプラットフォームで使われ、今も活発なレトロコミュニティがあることくらいだが、それは面白さの要素に近く、実際の関連性は低い
68020は命令セットアーキテクチャがCに近づけるほぼ最大限に近く、使い心地がとても良い
ただし、初期のCommodoreコンピュータで65XXを使うのは非常にやりがいがあった。メモリ保護がないためビデオメモリを直接書き換え、スプライト・フォント・ボーダー・割り込みをいじり、自己書き換えコードまで書けた。Amigaの68000アセンブリは、より安全で制御された環境だった
アセンブリを学ぶ最初の命令セットとしては、6502は良くないと考える。巧妙ではあるが根本的に欠陥のあるアーキテクチャの癖に対処することに多くの時間を使うことになる。
そうした回避のためのイディオムは、当時のMOSが持っていたツールや予算の制約に縛られない、より優れたアーキテクチャにはあまりつながらない。
小さくて強力で、少しだけ癖のある命令セットを学びたいなら、ARM v6Mのほうがよい。今でも意味のある形で生産されており、最新のオープンソースツールチェーンでもデバッガ・コンパイラ・アセンブラ・リンカのサポートが充実している。
アーキテクチャのオープンさを重視し、まだ未成熟なプラットフォームを受け入れられるなら、RISC-V MCUを選べばよい。決めきれないならRP2350を選べばよい。
ARMv6M命令セットは小さく、定数のロードも文書どおりにPC相対ロードを使えば長くて退屈な命令列は不要だ。自己書き換えコードやゼロページでメモリをインデックスする必要もなく、レジスタ幅はアドレス空間と同じだ。32ビットなので、むしろ大半の8/16ビット命令セットより学びやすく教えやすい。地味に聞こえるなら心配はいらない。ARMv6にもコードゴルフに使える癖は十分にある。
PIC16アセンブリから始めて、いくつかのアーキテクチャを少しずつ触ってきたが、整然さという点ではMIPS32がいちばん気に入っていた。
ちょっとした補足として、Western Design Centerが新たに作った6502や6502コアMCU、周辺チップは今でも買える。Mouserのようなところでも販売している。
https://www.mouser.com/c/?m=Western%20Design%20Center%20%28W...
16ビット整数2つを掛けるだけでも、かなりのコード量が必要になる。新しいプログラマがこうしたことに苦労するのは有益には見えない。
言われているように、初期のARM命令セットは良い選択だ。
もちろんこれはさまざまなアセンブリ言語にまたがる問題ではあるが、最初はこうした問題のない命令セットから始めるのがよいと思う。
[1] https://youtube.com/playlist?list=PLowKtXNTBypFbtuVMUVXNR0z1...
学部生に何年もアセンブリを教えてきた立場から言うと、6502が初心者に向いているという点には同意する。ただし、その理由が6502言語自体の長所だけにあるわけではない。
68K、MIPS、ARM、x86などを教えてきたが、6502を教えたときに学生の反応が良かった最大の理由は、CPUの周辺にある文脈だった。私たちは6502で理解しやすい実機、つまりNintendo Entertainment Systemをプログラミングしていた。
基本的なメモリマップドI/O、OSなし、パイプライン命令なし、ディレイスロットなし、ネットワークなし、余計なノイズなし。内部にクロック、CPU、いくつかのメモリアドレス、補助チップ、メモリアドレスにマップされたI/Oがある単純な箱に近い。6502が最も単純な命令セットというわけではないが、このシステムの単純さが大いに役立つ。
6502の制約も、学生が命令がなぜそのような形なのかを理解するうえで重要だった。CPUは当時の制約の中で設計され配線されており、その点がプログラミングのやり方に反映されている。
初学者教育に6502とNESを選んだのは成功だった。6502だったからというより、6502がビットを動かすシステムそのものを単純にするよう強制したからだ。
学生たちが6502に触れて、NESのタイルが画面上で動くのを見たあとは、68000がどう違って処理するか、MIPSがどう登場したか、パイプライニングとディレイスロットをどう活用するか、RISCとCISCの違いをどう比較するかへと自然に広げやすかった。基礎があれば発展はとても簡単になる。
現在使われている命令セットを学ぶのでなければ、PDP-11アセンブラが良い出発点としてほとんど挙がらないのは少し意外だ。
たぶん自分が最初にきちんと学んだのがPDP-11だったからかもしれないが、初期のマイクロプロセッサはどれも一歩後退したように感じられた。Z80アセンブリを何年か使ったことはあるが、今では勧めない。直交的な命令セットではないし、6502はレジスタが少なすぎて、アセンブリを書く感覚をきちんと得にくい。
PDP-11が好きなら、MSP430では似た長所をやや制限された形で、68000ではやや強化された形で得られる。
だが実際のところ、そうした遺物は忘れてRISC-Vを学ぶのが最善だ。あるいはArmのさまざまな派生のどれかを選べばよい。個人的には、2000年代半ばによく触っていたARM7TDMIに感情的な愛着がある。Thumbモードは元のArmモードより少し学びやすいかもしれないが、どちらもRISC-Vほど満足感はない。
アドレッシング方式がRISCより概念的に複雑だと主張することはできるが、実際には6502のアドレッシング方式のほうがPDP-11より理解しにくい可能性が高い。
だがDECはその機会を見過ごし、IBMが不格好な8086命令セットでその座を奪った。
もはや今、-11を学ぶ目的はない。
どうしてそんな結論になるのか、よく分からない。学生が初めてコンピュータプログラミングに触れるとき、実用性のない玩具的な小規模言語から始めるのはあり得る。だが、アセンブリは最初に学ぶ言語であるべきではない。
アセンブリは非常に実用的な道具で、普通はデバッグやリバースエンジニアリングでよく使われる。では、なぜとっくに消えたプラットフォームのアセンブリに時間を使うのか。
アセンブリを学ぶ最良の方法は実験だ。好きな言語でコードを書いて中間アセンブリ出力を見たり、objdump や gdb で内部をのぞけばいい。書き換えて、何が起きるか確かめることもできる。
昔のコンピュータのエミュレータでもできるが、もっと難しい。画面に文字を1つ出すだけでも、そのコンピュータのアーキテクチャ、ハードウェア、ROM の機能まで学ばなければならず、この知識は x86-64 の Linux や Windows にはほとんど応用できない。
完全な初心者にとって ポインタ のような概念は抽象的すぎて難解だったが、アセンブリで間接アドレッシングを学んだことで、C にポインタがなぜあり、どう動くのかが突然はっきりした。
それ以前は主に Python を使っていて、ずっと抽象化されていた。ポインタのような機能がハードウェアと性能上の制約のために存在することを、人は忘れがちだ。CPU の内部で実際に何がなぜ行われているのかを知らないと、直感的な理解には限界がある。
高校以来アセンブリを1行も書いていないが、演算がどう実行され、レジスタがどう動くのかという基礎理解のおかげで、プログラミングや最適化の理由、条件、例外をずっと理解しやすくなった。きれいで効率的なコードもより高く評価するようになった。
少し昔を味わいたいなら、ここにある。
https://www.atariarchives.org/
私は電子工学を学んでいたので、自然にアセンブリ(Motorola HC11)から始めた。課程の終わり頃には、みんなが独自に for ループのようなもののためのマクロを作るようになり、そこから自然に C へ移った。C の課程が終わる頃には C スタイルのオブジェクト指向も自然に現れ、次の C++ の課程につながった。
このアプローチの欠点は、関数型パラダイムや一般的な非命令型パラダイムへ至る段階的な道筋がないことだ。また、言語が内部でどう動くのかを常に考える癖がつくが、これが裏目に出ることもある。Haskell を学ぼうとしたときも、インタプリタがどう動くかから理解しようとしてしまった。
アセンブリを学ぶというのは、言語だけでなく、バスやメモリマップ周辺機器など、機械がどう動くのかを理解することでもある。古いプラットフォームではこの部分がずっと単純だ。だから ARM 命令のほうが HC11 の CISC 命令より学びやすいとしても、その他のすべてを含めれば、初心者には HC11 のほうが親切だ。
-vasmを付けてコンパイルすると、生成されたアセンブリをコンパイル中に表示してくれる。objdump や-Sを使えばいいだろうと不評だったが、実際に使うとなぜ便利か分かる。オブジェクトファイルを作るための膨大な定型句なしに、アセンブリだけをそのまま出力 してくれるからだ。たとえば AArch64 コードジェネレータ、より具体的には浮動小数点コード生成を作業中で、こんな関数がある。
float test(float a, float b) { return a * b; }dmd -c test.c -arm -vasmでコンパイルすると、アドレス、16進の命令語、命令ニーモニック、命令仕様の URL が出力される。コードが完全には正しくないことは分かっている。まだ作業中だと言ったはずだ :-)
私にとっては、6502 命令セットは良い最初のアセンブリ言語だった。
1977年に Oregon の小さな小屋に住んでいて、暇つぶしに Apple II を買った。1年もたたないうちに、後に Apple Writer となるプログラムに取り組むことになり、すべてアセンブリで書いた。
https://en.wikipedia.org/wiki/Apple_Writer
ここでは 6502 アセンブリはかなり粗野で書きにくいと言われているが、振り返ってみれば同意する。ただ、1977年当時は比べる基準がなかった。
Apple II には高速な高級言語がなかったので、私の小さなプログラムは、選択肢の乏しさのおかげで Apple 製品になった。
考えてみれば、Apple Writer は 8KB RAM に収まりながら、実際に仕事をしていた。人々が住所録を処理するために使っていたマクロ言語もあった。
最近、DeepSeek をローカルでより簡単に動かすためにメインシステムを 96GB RAM に増やし、RTX 4090 も載せた。ふと、この RAM には Apple Writer がほぼ1,200万本入るのだと気づいた。
かなり超現実的だが、1977年以降こういうことを言う機会は何度もあった。
入門用のアセンブリ言語としては RISC-V を好む。設計が良く、より直感的で、GCC、LLVM、Rust のような現代的な言語とツールのサポートがあり、QEMU と実際に購入可能なハードウェアで動くからだ。
しかも 14MHz で動作できる。
6502命令セットの単純さという話題は、個人的には引っかかる点だ。そうした単純さが良いと思う人は、Hello World以降へ進んだことがないように見える。
6502で中程度に複雑なものをプログラミングするのは難しい。8ビットは制約が強すぎるし、たとえばCommodore 64の画面アドレス指定を見てもそれが分かる。乗算と除算は自分で実装しなければならず、16ビットの加算/減算も単純ではあるが、効率よくやろうとすると決して些細ではない。
基本的なアセンブリを学ぶのが目的なら、8086+DOSプラットフォームのほうが比較にならないほど扱いやすい。
さまざまな乗算・除算アルゴリズムをアセンブリに移し替えるのは、学習にとても良い方法だ。
どうせx86-64、RISC-V、ARMではない、今では直接関係のないものを学ぶのなら、8086の利点が何なのかよく分からない。
その時間はずっと良かったし、セグメントレジスタもそれほど気にならなかった。
コンピュータサイエンス寄りの専門学校に通っているが、レトロ好きで自作アーケード筐体を見せるのが好きな情報学の教授が、所定の授業計画だった8808アセンブリの代わりに6502へ重点を置いた。
正直、人生で最も良い学習体験の一つだったし、別のやり方が良かったとは思わない。Ben Eaterのブレッドボードコンピュータまで作らされて、とくに手で直接触る感覚が強く、本当に面白かった。
初めて触れたアセンブリ言語はPDP-10で、手元にあったのはDEC-10プロセッサマニュアルだけだった。
完全に混乱した。命令は何百個もあり、説明は分かりにくかった。レジスタとは何か、アキュムレータとは何か、アドレスとは何か、スタックとは何か、まったく分かっていなかった。David Rolfeが、私のFortran版Empireに必要なサブルーチンをいくつか書いてくれて少し助けにはなったが、それでもなお道に迷ったままだった。
ある日、友人のShal Farleyにスタックとは何かと尋ねたところ、「皿の積み重ねを思い浮かべてみて。皿を一枚載せるのが(push)、一枚取り出すのが(pop)だ」と言われた。その瞬間、ぱっと腑に落ちた。
その後、小さなボード上の6800マイクロプロセッサを扱い始めた。命令は40個ほどで、カード1枚に全部収まっていた。40個の命令は覚えやすく、突然すべてが理解できた。
再び-10マニュアルに戻ると、書かれていることがすべて意味を成し始めた。