Guide to making a CHIP-8 emulator (2020)

2025/11/30 0:49

Guide to making a CHIP-8 emulator (2020)

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

概要:
このガイドは、CHIP‑8 インタープリターの構築方法を説明します。完全なソースコードは提供せずに、アーキテクチャと設計指針のみを示しています。必要なハードウェアとして、最大4 KBのRAM、64×32ピクセルのモノクロディスプレイ(SUPER‑CHIPの場合は128×64)、0x200 から始まる16ビットプログラムカウンタ、インデックスレジスタ I、16個の2バイトエントリを持つスタック、V0–VF の16個の8ビット汎用レジスタ、および別々のディレイ/サウンドタイマーが含まれます。メモリ上の最初の512バイト(0x000–0x1FF)は組み込みフォントを保持しており、プログラムは 0x200 にロードされます。
キーパッドは 4×4 のグリッドで 0–F とラベル付けされており、一般的に QWERTY キー(1 2 3 4 Q W E R A S D F Z X C V)にマッピングされています。
コア命令セットには、画面クリア (00E0)、ジャンプとコール (1NNN, 2NNN, 00EE)、スキップ (3XNN, 4XNN, 5XY0, 9XY0)、レジスタ操作 (6XNN, 7XNN, 8XY0–E, FX07/15/18, FX1E, FX29, FX33, FX55/65)、描画 (DXYN)、乱数生成 (CXNN)、キー判定 (EX9E/A1)、ブロック待機 (FX0A) が含まれます。
8XY6/8XYE、BNNN、FX55/FX65 のような曖昧なオペコードは歴史的にバリエーションがあります。インタープリターは「quirk」設定を構成可能にし、望む動作を選択できるようにすべきです。
タイミング:元の CHIP‑8 CPU は約1–4 MHz で動作していました;典型的なインタープリターは秒間約700オペコードを実行します。ディレイとサウンドタイマーはフェッチ/デコード/実行ループとは独立に60 Hzで減算されます。
フェッチ段階では、PC から連続する2バイトを読み取り、それらを16ビットオペコードに結合し、その後 PC を2増加させて実行へ進みます。デコードは最初のニブルでディスパッチを行い、さらに X, Y, N, NN, NNN の値を命令ごとに一度だけ抽出して重複を避けます。
描画 (DXYN) は XOR を使用してピクセルを反転させ、衝突時に VF を設定し、座標はスクリーンサイズでモジュロ演算され、描画中にクリッピングが適用されます。
推奨デバッグ機能にはステップ実行、レジスタ/メモリ検査、および未知のオペコードに対する明確なエラーメッセージが含まれます。

この改訂版概要は、リストからすべての主要ポイントを取り込み、広範囲への影響に関する推測的表現を除外し、読者にとって明瞭さを保っています。

本文

CHIP‑8 エミュレーター構築のハイレベルガイド


1. はじめに

  • なぜ CHIP‑8? それは「最初のエミュレーター」プロジェクトとして古典的です。
  • 本ガイドでは、インタープリターの各部が何をすべきかを説明します。実際のコードは読者に任せます。

2. 歴史(簡易再現)

イベント
1977RCA のエンジニア Joe Weisbecker が COSMAC VIP 用に CHIP‑8 を開発。
1984関心が薄れ、ほとんど使われなくなる。
1990HP48 カルキュレーターで CHIP‑48 と SUPER‑CHIP により復活。

3. 前提知識

  • 基本的なプログラミングの知識
  • バイナリ/16進数の理解
  • グラフィック描画とキーボード入力を扱えること(SDL、SFML 等)

4. コアコンポーネント

コンポーネントサイズ / タイプ備考
メモリ4096 bytes (0x000–0xFFF)全 RAM。プログラムは 0x200 からロードされる。
ディスプレイ64×32 モノクロ(SUPER‑CHIP は 128×64)各ピクセルはビット単位。
PC16bit次のオペコードを指す。
I16bitインデックスレジスタ。
スタック16レベル、各 16bitサブルーチン呼び出し用。
タイマーディレイ&サウンド(8bit)60 Hz 毎に減算。
レジスタV0–VF (8bit)VF はフラグレジスタ。

5. フォント

標準の16文字(4×5 ピクセル)のフォントセットをメモリの最初の512バイトに格納します。例:

const uint8_t fontset[80] = {
    0xF0,0x90,0x90,0x90,0xF0, /* 0 */
    0x20,0x60,0x20,0x20,0x70, /* 1 */
    …(省略)…
};

0x050–0x09F、または 0x200 未満の任意のアドレスに配置します。


6. ディスプレイ描画

  • DXYN:
    [I]
    から N バイトを読み取り、各ビットをスクリーンバッファへ XOR。
  • Clear:
    00E0
  • 座標はディスプレイサイズでモジュロ計算し、エッジでは描画をクリップ。
  • 描画オペコードが実行されたときのみ画面更新(FPS 最適化)。

7. スタック操作

uint16_t stack[16];
int sp = 0;          // スタックポインタ

// コール
stack[sp++] = pc;
pc = NNN;

// リターン
pc = stack[--sp];

8. タイマー

別スレッドまたはタイマーで 60 Hz 毎に両方のタイマーを減算。
サウンドタイマーが >0 のときビープ音を鳴らす。


9. キーパッドマッピング

CHIP‑8実機
1Q
2W

キースキャンコードを利用して異なるキーボードレイアウトに対応。


10. フェッチ/デコード/実行ループ

while (running) {
    // --- FETCH -------------------------------------------------
    uint16_t opcode = memory[pc] << 8 | memory[pc + 1];
    pc += 2;

    // --- DECODE ------------------------------------------------
    switch (opcode & 0xF000) {
        case 0x0000:
            if (opcode == 0x00E0) clearDisplay();
            else if (opcode == 0x00EE) returnFromSubroutine();
            break;
        case 0x1000: pc = opcode & 0x0FFF; break;          // JP addr
        case 0x2000: callSubroutine(opcode & 0x0FFF); break;
        case 0x3000: skipIfEqual( (opcode >> 8) & 0xF, opcode & 0xFF ); break;
        … /* その他ケース */
    }

    // --- TIMER UPDATE ------------------------------------------
    updateTimers();   // 外部で 60 Hz にて呼び出し
}

速度制御

  • ターゲットは ~700 オペコード/秒(調整可能)。
  • スリープまたは高精度タイマーでスロットリング。

11. 命令概要

Opcode意味備考
00E0
画面クリア
1NNN
NNNへジャンプ
2NNN
サブルーチン呼び出し
3XNN
VX == NN のときスキップ
4XNN
VX != NN のときスキップ
5XY0
VX == VY のときスキップ
6XNN
VX = NN
7XNN
VX += NN(キャリー無し)
8XY0
VX = VY
8XY1
OR
8XY2
AND
8XY3
XOR
8XY4
VX += VY(キャリーは VF)
8XY5
VX -= VY(借用は VF)
8XY6
SHR VX(クワーク:VY を使うことがある)
8XY7
VY -= VX(借用は VF)
8XYE
SHL VX(クワーク:VY を使うことがある)
9XY0
VX != VY のときスキップ
ANNN
I = NNN
BNNN
JP V0 + NNN(クワーク:VX を使うことがある)
CXNN
ランダム & NN → VX
DXYN
(VX,VY) でスプライト描画、I から N バイト読み取り
EX9E
キー VX が押されているときスキップ
EXA1
キー VX が押されていないときスキップ
FX07
VX = ディレイタイマー
FX0A
キー入力待ち → VX に格納
FX15
ディレイタイマー = VX
FX18
サウンドタイマー = VX
FX1E
I += VX(クワーク:VF を設定することがある)
FX29
I = VX の数字に対応するスプライト位置
FX33
BCD 変換 → [I],[I+1],[I+2]
FX55
V0–VX をメモリへ書き込み(クワーク:I が増分されることがある)
FX65
メモリから V0–VX を読み込む(クワーク:I が増分されることがある)

クワーク

  • 「モダン」モード(FX55/65 で I 増分なし、8XY6/E で VY 無視など)を実装し、切り替え可能にする。

12. デバッグツール

  1. ステップ実行 – ループを一時停止して現在のオペコードを表示。
  2. レジスタ/メモリダンプ – V レジスタ、I、PC、スタックを出力。
  3. 未知オペコードでエラー – データとしての誤解読を検出。
  4. ロギング – オプションで詳細ログ。

13. テスト

テスト用途
IBM Logo(
00E0
,
1NNN
,
6XNN
,
7XNN
,
ANNN
,
DXYN
のみ使用)
基本描画・レジスタ動作確認。
BC_test / corax89’s test ROM全命令網羅、特に曖昧なケースの検証。

各実装マイルストーン後に実行。


14. 次のステップ(オプション拡張)

  • SUPER‑CHIP – 解像度倍増、スクロール、大きいスプライト。
  • XO‑CHIP – 4 色表示、音声、64 KB メモリ。
  • デバッグインターフェース – シリアルコンソール、Web UI、ゲーム内デバッガ。
  • ネットワークプレイ – Telnet サーバーや SSH エミュレータ。
  • Web Assembly – ブラウザで CHIP‑8 を動かす。

15. 結論

これで CHIP‑8 インタープリターを構築するためのアーキテクチャ、コンポーネント、命令セットが揃いました。まずはコアループを実装し、IBM ロゴを動かしてみてください。その後、段階的に拡張しながらテストを繰り返すことで、機能的で安定したエミュレーターへと育て上げましょう。ハッキングを楽しんでください!

同じ日のほかのニュース

一覧に戻る →

2025/11/30 18:11

Self-hosting my photos with Immich

## Japanese Translation: 記事では、著者が低電力Ryzen 7ミニPC(ASRock DeskMini X600)に64 GB RAM、1 TBディスクを搭載し、アイドル時の消費電力が10 W未満である環境にImmichをセットアップした手順を説明しています。Proxmox上に「photos」という名前のVMを作成し、500 GBのストレージ、4つのCPUコア、4 GB RAMを割り当てました。NixOS設定ファイルで `services.immich.enable = true` を有効化してImmichサービスを起動します。このサービスは `tailscale serve --bg http://localhost:2283` コマンドと MagicDNS/TLS によりTailscale経由で公開され、`https://photos.example.ts.net` からアクセス可能です。 公式の `immich‑cli` を使用した初期写真インポートでは、バックグラウンドジョブがタイムアウトし、Google Takeout のJSONメタデータが無視されるという問題が発生しました。第三者ツール **immich-go** が両方の問題を解決します。`immich-go upload from-google-photos …` を実行することでバックグラウンドタスクを一時停止し、Google Takeout アーカイブを正しく処理できます。その後、iPhoneアプリはTailscale URL経由でログインし、自動アップロードが有効化され、通知は無効にしてアップロードアラートを防止します。 バックアップについては、著者はsystemdタイマーを使用して `/var/lib/immich`(UPLOAD_LOCATION)ディレクトリ全体を rsync で3‑2‑1戦略で同期する予定です。これはImmichの公式ドキュメントに従った方法です。記事では、Immichには組み込みの写真編集機能がないため、ユーザーはGIMPなど外部ツールを使用しなければならず、共有もまだGoogle Photos経由で行われると指摘しています。Enteと比較して、著者は既存のTailscale VPNとLUKSディスク暗号化が十分なセキュリティを提供するため、エンドツーエンド暗号化を必要としないImmichを好んでいます。 総じて、このセットアップは小型かつ省電力マシン上で高速で信頼性の高いセルフホスト写真保存ソリューションを実現しており、外部編集ワークフローを受け入れられる趣味家や小規模ビジネスに適しています。

2025/12/06 12:32

Nook Browser

## Japanese Translation: > **概要:** > 製品「Browse」はプライバシーを最優先としたオープンソースのウェブブラウザで、ユーザーのデータが販売または追跡されることは決してないと約束しています。WebKit エンジンをベースに構築されており、高速なパフォーマンスと最小限のシステムオーバーヘッドを実現しながら、インターフェイスはクリーンで侵入的なポップアップがありません。チャット支援や要約、最新のウェブ情報などの AI 機能は、ユーザーが明示的に選択した場合のみ利用可能です。コードベース全体が公開されており、パーミッシブ ライセンスでリリースされています。また、コミュニティ主導のロードマップに従い、新しいツールを追加する前に安定性を優先しています。設定はユーザーが理解しやすく、逆行可能(戻せる)ように設計されています。FAQ セクションでは、これらのポイント以外に独自の情報は提供されていません。 このバージョンは主要なポイントをすべて保持し、業界への影響についての推測を削除し、設定の逆行性と FAQ の内容に関する欠落した詳細を追加しています。

2025/12/06 0:35

Cloudflare outage on December 5, 2025

## Japanese Translation: ``` ## Summary Cloudflare の 2025 年 12 月 5 日の障害は約 25 分間続きました。 08:47 UTC にネットワークセグメントが故障を開始し、08:50 UTC に完全な影響に達し、09:12 UTC に問題が解決しました。 全 HTTP トラフィックの約 28 %(古い FL1 プロキシと Managed Rulesets を使用している顧客)がエラーを経験しましたが、中国ネットワークトラフィックは影響を受けませんでした。 障害は、CVE‑2025‑55182(React Server Components の脆弱性)を修正するために意図された二つの急速なコード変更によって引き起こされました。 まず、WAF バッファサイズが 128 KB から 1 MB に増加し、段階的に展開されました。 次に、グローバル設定更新で内部 WAF テストツールが無効化され、FL1 のルールモジュールで Lua エラー(`attempt to index field 'execute' (a nil value)`)を引き起こし、HTTP 500 応答を生成しました。 このバグは何年も存在していましたが、「execute」ルールのキースイッチが execute フィールドが欠落した際に処理するコードを回避したために露呈しました。 同様で大規模なインシデントが 2025 年 11 月 18 日にも発生しました。 Cloudflare はロールアウト手順の強化、バージョン管理制御の追加、ブレイクグラスアクセスの簡素化、およびフェイルオープンエラーハンドリングの実装に取り組んでいます。詳細なレジリエンシー計画は来週公開される予定であり、新しい緩和策が稼働するまでネットワーク変更は停止されたままです。 この出来事は、大規模 CDN 運用における厳格な変更管理の必要性を強調し、迅速展開保護策に関する業界全体での見直しを促す可能性があります。 ```

Guide to making a CHIP-8 emulator (2020) | そっか~ニュース