こんにちは、パレイド技術部の夏目です。ファミリーベーシック V3(任天堂、1985年)の上で、AI にマシン語(CPU 6502 が直接実行する命令列)を書かせ、スーパーマリオ 1-1 を再現したマリオ風クローンを動かしました。動画「ファミリーベーシックでマシン語に挑戦」の後編の受け記事として、動画で語り切れなかった数字の表と、打ち込み方を足します。テトリス風の前編は別の記事で扱っています。
https://youtu.be/uDjcJF1PE2I
任天堂とは無関係の、個人の制作物です。確認はすべてエミュレータ上で、実機・実テープでは確かめていません。
本記事はローカル LLM による自動執筆パイプラインで生成されました。現段階ではクラウド AI(Claude 等)の補助や人間の編集が介在していますが、pareido.jp では最終的に AI が自律的にコンテンツを制作できる仕組みの構築を目指しています。
原作の動作から取った数字
前提の条件を先に並べます。絵は自作せず、ファミリーベーシックの ROM に入っているキャラの組み合わせだけで描いています。
| 項目 | 条件 |
|---|---|
| 対象 | ファミリーベーシック V3 |
| 範囲 | 1-1 のみ |
| 使える RAM | $6000〜$6FFF の 4,086 バイト(BASIC の本文と共有) |
| 絵 | ROM 内蔵のキャラの組み合わせ(自作の絵なし) |
| 確認 | エミュレータ上(実機・実テープは未確認) |
点数とパレットは、記憶ではなく原作をエミュレータで動かして実測しました。連続で踏んだときの点数に「500 なんてあった?」と疑問が出たのが発端で、ROM を動かすと確かに入っていました。
| 項目 | 実測した値 |
|---|---|
| 連続踏みの点数 | 100→200→400→500→800→1000→2000→4000→5000→8000 |
| 2匹を同時に踏む | 連鎖が2つ進み、1回目が 500 |
| 甲羅で倒す | 500→800→1000→2000 |
| マリオの色 | $27 / $16 / $18 |
雲・茂み・丘の配置は、原作を最後まで走らせてネームテーブル(背景のタイル番号の並び)を採取し、そのまま取り込みました。
実装で踏んだ罠は3つです。
- 背景の色0は白にできない。色0は全パレット共通の背景色で、パレットを変えても変わりません(PPU=画面を描く専用チップの仕様)
- 画面が真っ黒になる。NMI(画面を1枚描き終えるたびに CPU を呼ぶ割り込み)を自前に差し替えると、BASIC が
$32/$33に持つ画面設定を自分で書き戻す必要がありました - 画面の上端が乱れる。スクロールの計算を NMI の中で行うと描画の休み時間に収まらず、計算はメインの処理、NMI は書き込みだけに分けて解消しました
なお動画のクローンは自動操縦で、原作より速く走ります。同じ速さの比較ではありません。
3.6KB が 4KB に入らない壁とローダ
ファミリーベーシックには、マシン語をそのまま保存・読み込みする手段がありません。マシン語は DATA 文に16進で書き、POKE でメモリに置き、CALL で呼びます。その BASIC の本文も同じ 4KB を使うので、3.6KB のマシン語は一度に入りません。
そこでパートに分け、LOAD と RUN を繰り返して積み上げます。鍵は CLEAR &Hxxxx です。これは BASIC の本文の領域と、その上の自由に使える領域を分ける命令で、上の領域に書いた値は NEW でも LOAD でも、CLEAR の値を下げても残ることを実測で確かめました。積み上げが少ない最初のパートほど本文を大きく取れるので、CLEAR を後ろのパートほど下げます。
| ローダ | マシン語 | パート数 | 読み込み時間 | 判定 |
|---|---|---|---|---|
| 最初の分け方 | 3,632B | 31 | 1,157秒 | △ 起動と走行は確認。打ち込むには多すぎる |
CLEAR を後ろほど下げる |
3,632B | 8 | 881秒 | ○ 起動と走行を確認 |
| 公開コード(改良後) | 3,746B | 10 | 未計測 | ― |
読み込み時間は、エミュレータへのキーボード入力での実測です。最初の分け方では、マシン語が 100 バイト増えるごとにパートが約5つ増えていました。
動画では「3,632 バイト・8パート」と説明しましたが、公開するコードは「3,746 バイト・10パート」です。動画の作成後に機能を足して改良したためで、10パート版の読み込み時間は測っていません。公開コードの CLEAR の階段は次のとおりです(1パートの量は15バイト単位。3,746 バイトを 3,750 バイトに切り上げ、余りは0で埋めています)。
| パート | CLEAR |
置く量 |
|---|---|---|
| 01 | &H6B50 |
1,200B |
| 02 | &H6808 |
840B |
| 03 | &H65CE |
570B |
| 04 | &H6448 |
390B |
| 05 | &H633A |
270B |
| 06 | &H6286 |
180B |
| 07 | &H620E |
120B |
| 08 | &H61B4 |
90B |
| 09 | &H6178 |
60B |
| 10 | &H615A |
30B |
各パートの先頭は同じ形です。mario_01.bas の冒頭を引きます。
10 CLEAR &H6B50
20 A=&H6B50:FOR K=1 TO 80:READ S$:FOR J=1 TO 30 STEP 2
30 POKE A,VAL("&H"+MID$(S$,J,2)):A=A+1:NEXT:NEXT
10行目で置き先を守り、20〜30行目で DATA の16進文字列を1バイトずつ POKE します。打ち込み方は次のとおりです(公開先の README と同じ)。
mario_01.basを打ち込んでRUN。書き込みが終わるまで待つNEWで本文を消し、次の番号を打ち込んでRUN- 最後のパート(
mario_10.bas)まで繰り返す。最後のパートのCALLでゲームが始まる - 先頭の
CLEARの値は変えない
コードは https://pareido.jp/files/famibasic-machinecode/README.md から辿れます。公開しているのは 4KB に収まる版だけで、動画の最後に出る約 9.6KB のディスク版(ディスクベーシック V3.0P 用)は BASIC のローダにできないため含まれません。4KB の壁そのものは、以前の記事でも扱っています。
キャラ近似ツール:数字と見た目を分けて
人の手で一番重かったのは、原作の絵に近いタイルの組み合わせを探す作業でした。そこで AI に近似ツールを作らせました。スプライトは小マリオの12ポーズを、反転4通りと並べ替えの全組み合わせで比べます。背景は 1-1 の31種の升目を、「16×16 ごとに4組のパレットから1組」「色0は共通」という制約を守って近似します。
物差しは3つです。違う画素は色が違う画素の数(256 中、小さいほど近い)、ぼかし RMSE はぼかした色の差(小さいほど近い)、SSIM は形や模様の構造の近さ(1 で同一)です。背景の結果は、クローンが描く 794 升目の平均で次のとおりでした。
| 背景の選び方 | 違う画素 | ぼかし RMSE | SSIM |
|---|---|---|---|
| ツール(画素で最適化) | 71.9 | 37.7 | 0.46 |
| ツール(見た目で最適化) | 91.8 | 27.4 | 0.45 |
| ツール(原作パレット) | 93.4 | 28.8 | 0.45 |
| 人の手選び | 140.4 | 44.3 | 0.17 |
数字ではツールが近いのに、画像で見ると逆でした。ツールは地面やブロックに土管の縁や縦縞のタイルを選び、何の絵か分からなくなります。手選びの地面 $CC・レンガ $C3 は数字では遠い(SSIM 0.06・-0.2)ものの、質感は自然です。ファミリーベーシックの絵のタイルは105枚しかなく、原作の質感に近いものが少ないのが理由と考えています。
スプライトはポーズで分かれました(ツール欄は見た目で選んだ1位)。
| ポーズ | ツール | 既存のマリオ | 見た目の判定 |
|---|---|---|---|
| 歩き3 | RMSE 15.3・重なり 88% | 21.1・84% | ○ ツールが原作にかなり近い |
| 立ち | RMSE 22.9 | 28.8($00-03) |
× 既存のほうが自然 |
| ジャンプ | RMSE 26.9 | 38.3($0C-0F) |
× 腕を上げた既存のほうが近く見える |
「重なり」はシルエットの重なりの割合です。色については、パレットの移植はツールの圧勝でした。原作と同じ4組のパレットを使っても、パレットまで最適化した場合との差は小さく(違う画素 76.0 対 71.9)、原作のパレットがファミリーベーシックのタイルでもほぼ最適です。
採用したのは人が選んだ組み合わせです。ツールは升目ごとに上位5件の候補を並べる道具にとどめました。物差しは画素・色・構造を測れても、「人が見てそれらしいか」は測れません。
まとめ
できたのは、1-1 の通しクリア(自動操縦・ミス0)、4KB の壁を抜けるローダ、候補を出すキャラ近似ツールです。動画の時点でポール滑り・コイン・タイム・マリオの BGM は未実装で(公開コードもこの範囲です)、実テープでの保存と読み込みも未確認です。原作は絵が 8KB、プログラムが 32KB あり、全部を 4KB に収めるのは難しいでしょう。
マシン語を AI に書かせるための前提は、機械語対応(2) にまとめています。容量の壁の先は、ディスクで動かす別の回で扱う予定です。