Rustで創る MOS 6502 CPU その3 (Cursorと共に)
2025.02.24
どもです。
去年書いていた「
Rustで創る MOS 6502 CPU その2
」の続きとなります。
まぁ、間が空きすぎてのでどこまでやっていたかもあやふやだったのですが、確かopcodeも途中だったはず。
ということで、今回はopcodeを最後まで書いて行きたいのですが、手書きで書いていくのもしんどくなって来ました。と、言うのも最近では専らAI駆動開発を行っているということもあり、大枠などは大抵AIに作成してもらっている今日此の頃です。
いやぁ、楽ですね。あんまり考えなくなるのではないかと心配ではありますが、まぁあとから直せば良いかとAIさんにお任せしております。
そこで、今回から「Cursor – The AI Code Editor」のcomposerで指示を与えながら作成していこうではないと言うことで、このシリーズの再開を行いたいなと(ちょっと飽きて来たのもあって早く終わらせたいなんてことは全然思っていません。。)。
ということで、早速、こちらのドキュメントを「obelisk-6502-guide」をCursorさんの方に喰わせて、シンボリック機能を用いてcomposerで指示をしていきます。
@nes6502 を参考にして、 cpuのopcode0x01から続きを作成してください。
すると、
6502プロセッサのオペコードを実装していきましょう。まず、0x01の次のオペコードを追加します。
とのこと。
どかどか追加していってくれます。
また、
また、ASLの実装を修正する必要があります。現在の実装では結果を常にアキュムレータに格納していますが、メモリアドレスに書き込む場合もあるため、値を返すように変更します:
とのことで、
asl命令あたりも修正が入ります。「お、おうよ。」
これらの変更により、以下のオペコードが実装されました: 0x05: ORA Zero Page - ゼロページアドレスのメモリの値とアキュムレータのORを実行 0x06: ASL Zero Page - ゼロページアドレスのメモリの値を左シフト 0x08: PHP - プロセッサステータスをスタックにプッシュ 0x0A: ASL Accumulator - アキュムレータの値を左シフト 0x0D: ORA Absolute - 絶対アドレスのメモリの値とアキュムレータのORを実行 続けて他のオペコードも実装していきましょうか?
と一瞬で作成してくれる。
いやぁ。楽だ。しかし頭に入っていないなぁ(各オペコードを覚えようと思っていたので)
とはいえ非常に楽なので「続けてopcodeを作成してください」と入力し残りのopcodeを作成してもらう。
まず、StatusRegisterにas_byteメソッドが必要なのでそれを追加し、その後新しいオペコードを実装していきましょう。
また、StatusRegister構造体にas_byteメソッドを追加する必要があります。register.rsファイルに以下のコードを追加してください:
impl StatusRegister { pub fn as_byte(&self) -> u8 { let mut result = 0u8; if self.c { result |= 0b0000_0001; } if self.z { result |= 0b0000_0010; } if self.i { result |= 0b0000_0100; } if self.d { result |= 0b0000_1000; } if self.b { result |= 0b0001_0000; } // bit 5は常に1 result |= 0b0010_0000; if self.v { result |= 0b0100_0000; } if self.n { result |= 0b1000_0000; } result } }
と、こちらが追加されました。
追加したオペコードの説明: 0x0E: ASL Absolute - 絶対アドレスのメモリの値を左シフト 0x10: BPL - N(負)フラグがクリアされている場合に分岐 0x11: ORA Indirect,Y - インデックス付き間接アドレスのメモリの値とアキュムレータのOR 0x15: ORA Zero Page,X - Xレジスタでオフセットされたゼロページアドレスのメモリの値とアキュムレータのOR 0x16: ASL Zero Page,X - Xレジスタでオフセットされたゼロページアドレスのメモリの値を左シフト 0x18: CLC - キャリーフラグをクリア 0x19: ORA Absolute,Y - Yレジスタでオフセットされた絶対アドレスのメモリの値とアキュムレータのOR
続けて他のオペコードも実装しましょうか?
ふむふむ。「続けてopcodeを作成してください」を繰り返す。
はい、続けてオペコードを実装していきます。 また、StatusRegisterにfrom_byteメソッドを追加する必要があります:と
とのことで、
pub fn from_byte(byte: u8) -> Self { StatusRegister { c: (byte & 0b0000_0001) != 0, z: (byte & 0b0000_0010) != 0, i: (byte & 0b0000_0100) != 0, d: (byte & 0b0000_1000) != 0, b: (byte & 0b0001_0000) != 0, // bit 5は無視 v: (byte & 0b0100_0000) != 0, n: (byte & 0b1000_0000) != 0, } }
こちらが追加される。
追加したオペコードの説明: 0x1D: ORA Absolute,X - Xレジスタでオフセットされた絶対アドレスのメモリの値とアキュムレータのOR 0x1E: ASL Absolute,X - Xレジスタでオフセットされた絶対アドレスのメモリの値を左シフト 0x20: JSR - サブルーチンにジャンプ(戻りアドレスをスタックに保存) 0x21: AND Indirect,X - インデックス付き間接アドレスのメモリの値とアキュムレータのAND 0x24: BIT Zero Page - ゼロページアドレスのメモリの値とアキュムレータのビットテスト 0x25: AND Zero Page - ゼロページアドレスのメモリの値とアキュムレータのAND 0x28: PLP - スタックからプロセッサステータスを復元 0x29: AND Immediate - 即値とアキュムレータのAND 続けて他のオペコードも実装しましょうか?
ふむふむ。「続けてopcodeを作成してください」を繰り返す。
はい、続けてオペコードを実装していきます。 また、rol命令の実装を修正する必要があります。現在の実装では結果を常にアキュムレータに格納していますが、メモリアドレスに書き込む場合もあるため、値を返すように変更します:
ROLが修正される
fn rol(&mut self, value: u8) -> u8 { let old_carry = self.status.c; self.status.c = (value & 0x80) != 0; let result = (value << 1) | (old_carry as u8); self.status.z = result == 0; self.status.n = (result & 0x80) != 0; result }
そして
追加したオペコードの説明: 0x2A: ROL A - アキュムレータを左ローテート 0x2C: BIT Absolute - 絶対アドレスのメモリの値とアキュムレータのビットテスト 0x2D: AND Absolute - 絶対アドレスのメモリの値とアキュムレータのAND 0x30: BMI - N(負)フラグがセットされている場合に分岐 0x31: AND Indirect,Y - インデックス付き間接アドレスのメモリの値とアキュムレータのAND 0x35: AND Zero Page,X - Xレジスタでオフセットされたゼロページアドレスのメモリの値とアキュムレータのAND 0x36: ROL Zero Page,X - Xレジスタでオフセットされたゼロページアドレスのメモリの値を左ローテート 0x38: SEC - キャリーフラグをセット 0x39: AND Absolute,Y - Yレジスタでオフセットされた絶対アドレスのメモリの値とアキュムレータのAND 続けて他のオペコードも実装しましょうか?
繰り返すのが面倒なので、「残りのオペコードすべて作成してください」とする。
すると、
はい、リンターエラーを修正しながら、残りのオペコードを実装していきます。まず、lsrとrorの実装を修正し、戻り値を返すように変更します:
これで6502プロセッサの主要なオペコードがすべて実装されました。各命令の実装は以下のような特徴を持っています: メモリアドレッシングモードに応じた適切なアドレス計算 フラグの適切な設定 レジスタやメモリの更新 プログラムカウンタの適切な更新 これらの実装により、6502プロセッサの基本的な命令セットが完成しました。
おお。まじかよ。
取り敢えず徐ろに、cargo runで起動するか確かめる。
CPU { a: 0, x: 0, y: 0, pc: 0, sp: 255, status: StatusRegister { c: false, z: false, i: false, d: false, b: false, v: false, n: false } }
エラーなく、いっちゃっているな。ほぼ、「続けてopcodeを作成してください」
しか言っていないのに作成されてしまった。いやぁ楽だ。
■ 今回の追加コミット
https://github.com/retrodig/mos6502r/commit/4414fce6cb87a81619b223edfe35412dd80e774a
この快適さを知ってしまうともう抜け出せない。。。
次回からもCursor様といっしょに制作を進めていきます。
ではではぁ。