こんにちは、パレイド辺境部の橘です。
前回は、ファミリーベーシックの画面で「CLAUDE」とのチャットまできました。 いよいよ、BASIC 画面でのAIとの連携に進みます。
チャットパネルを足す
VS Codeのような編集画面をイメージし、nesemu のウィンドウシステムに、小さなチャットパネルを 1 枚追加します。 ブラウザの画面は左にファミリーベーシック、右に AI チャット、という並び。最近は逆の配置も増えてきていますね。

入力欄は小さめにして、小技ですが HTMLの contenteditable の div で実装しました。
ブラウザの keydown / keyup は nesemu 側で拾われてしまうため、一般的な textarea は使いにくたいめです。
// src/app/chat_wnd.ts より抜粋
this.input.contentEditable = 'plaintext-only'
for (const ev of ['keydown', 'keyup', 'keypress'] as const) {
this.input.addEventListener(ev, e => e.stopPropagation())
}
バックエンド
LLM への問い合わせは、ブラウザから直接 API を叩くのではなく、別立ての Node.js サーバーを経由させます。

nesemu-ai/phase1-chat-typing/server.ts が POST /api/chat を受けて、claude や Ollama 等のLLMに中継します。
provider は起動時に切り替えられますが、claude 以外での実用性は未知数です。
nesemu に AutoTyper という小さなコードを書いて、チャットパネルからのデータを受け取りキーボード入力に変換します。
40 行のシステムプロンプト
Claude や、多くのコーディング支援対応の LLM は BASIC も対応可能です。 ただあくまで一般的なBASIC 言語であり、制約の多いファミリーベーシックでは、動くコードはまず出力できません。システムプロンプトに40 行ほど、制約を書き込んでみます。 2KB RAM、行番号 10 刻み、変数名は英字 1 文字 + 数字 1 文字、整数のみ、浮動小数点関数なし、日本語不可、画面は 28 列 × 24 行、など。
こちらは例ですが、V3 というバージョン指定は率直なところ儀礼的です。そのほうが格好いい。
あなたはファミリーベーシック(Family BASIC V3)のプログラマーです。
ユーザーの指示に従って、Family BASIC V3 で動作するBASICプログラムを生成してください。
## 絶対に守るべき制約
- **整数のみ**: 小数は使えない。0.5 や 1.5 などの小数リテラルは構文エラーになる。
固定小数点が必要な場合は100倍スケール等で代用すること(例: 0.5 → 50, 掛け算後に /100)。
- **浮動小数点関数なし**: SIN, COS, SQR, LOG, EXP, ATN, INT 等は存在しない。
- **数値範囲**: -32768〜32767(16bit符号付き整数)。オーバーフローに注意。
- **変数名**: 英字1文字 + 数字0〜1文字(A, A0〜A9, B$, etc.)。長い変数名は不可。
- **文字列長**: 最大31文字。
- **メモリ**: V3で約4KB。プログラムは短く保つこと。
- **日本語**: ひらがな・漢字は使えない。英数字とカタカナのみ。
## 出力ルール
1. BASICコードのみを出力してください。説明は最小限に。
2. 各行は必ず行番号から始めてください。
3. プログラムは短く保つこと。目安は30行以内。
4. 行番号は10刻みで(10, 20, 30, ...)。
5. コードブロックは ```basic と ``` で囲んでください。
6. RUN と入力すれば実行できる状態にしてください。
## 例
ユーザー: 星を3つ描いて
```basic
10 CLS
20 FOR I=1 TO 3
30 X=RND(25)+1
40 Y=RND(20)+1
50 LOCATE X,Y
60 PRINT "*"
70 NEXT
80 END
AI 側はこの制約を受けて、「Family BASIC のプログラマー」として振る舞い、30 行以内の BASIC を ```basic ブロックで返してきます。few-shot として例も1つ与えています。

「星を描いて」で動作確認
ここまで順調に連携が進みました。最初のテストは、AI の挙動確認も含めて、確実に答えが来るはずの短めの指示をしてみます。
User: 星を描いて
数秒後にチャットにコードが返ってきます。

一字一句、few-shot の通りとはいえ、昔の教本でよく見るような内容に、郷愁を感じざるをえません。
ファミリーベーシックはグラフィックにドットを書くことはできませんので、BGグラフィックで星のパターンを使うか、スプライトで星のように見えるパターンで近似するか、このようにテキストで表示するパターンになります。アスタリスクはその名の示す通り立派な星ですね。
ちなみに、デフォルトのLLMでは、Claude ですら「INT」関数を使おうとして ‘?SN ERROR’ が出ます。 もちろん学習データは期待できませんし、プロンプトにより「整数のみ」という制約を与えても、しばしば無視されます。現代の AI には、ファミリーベーシックは一周して難しいようです。
自動タイピング
生成されたコードは、チャット欄の「Type into FB」ボタンを押すと、1 文字ずつ BASIC のキーボードに送り込みます。
実装はシンプルで、ASCII 文字を Family BASIC のキーの KeyType に変換する CHAR_MAP を先に作っておき、Keyboard.setKeyState() を押下→離しで叩いていきます。ブラウザのキー操作そのもので、あとは nesemu が処理をしてくれます。
// 抜粋
const KEY_DOWN_FRAMES = 3
const KEY_UP_FRAMES = 1
for (const ch of code) {
const stroke = CHAR_MAP[ch]
keyboard.setKeyState(stroke.key, true) // 押下
await waitFrames(KEY_DOWN_FRAMES)
keyboard.setKeyState(stroke.key, false) // 離す
await waitFrames(KEY_UP_FRAMES)
}
キー押下を3フレーム、離す時間を1フレームとおきます。理屈では、60fps 換算で 1 キー入力あたり約 66ms、秒間 20 文字くらいの速さで BASIC の画面に文字が流れ込みむ計算で。 10 CLS から順に 80 END まで、1 文字ずつ打ち込まれていきます。不思議と心が落ち着く光景です。
RUN
ここで「Type into FB」ボタンで、プログラムを送信できます。RUN すると星空?が表示されます。

画面内のランダムな位置に、星が 5 つ。といってもアスタリスクですが。2026 年の AI が書いた 30 行足らずのプログラムが、1984 年の仮想マシンで実行された、ということになります。
それだけと言えばそれだけですが、わたしは「星を描いて」とAIに一言送っただけ。あとは AI と BASIC が勝手に話していました。心に描いた光景がここにあります。
長くなるとキーの取りこぼしが発生する
ただ、これは短いプログラムでの話です。よくあるサンプルで、もう少し長い「数字当てゲーム」「ボールが跳ねるアニメーション」など頼むと、AI が 20 行、30 行のプログラムを返し、自動タイピングの途中で取りこぼしが起き始めます。

原因は、キーを押しているタイミングと、Family BASIC のキーボードスキャンが微妙にずれるからです。特にファミリーベーシックの処理が重い改行直後で行番号欠落が多発し、RUN すると ?SN ERROR の嵐。単純に十分なウェイトを確保するやり方では、安定はしますが、遅くて使い物になりません。
次回
AIとファミリーベーシックの「コネクタ」が安定しないと、バイブコーディングができても往年のパソコン雑誌のように手で打ち込む必要が出てきます。次回は、このキーの取りこぼし問題の解消に挑みます。


