Phosphoric › 記事 › LOCI 拡張
Oric の限界を押し広げる:LOCI カード向けの 5 つの実験的拡張
サイクル精密エミュレータをハードウェアの試験台とする方法 — ABI、レジスタ、そして回路図とともに。
写真について。 LOCI カードの図版は sodiumlb の公式ハードウェアドキュメント (loci-hardware) に由来し、その著者に帰属します。
- LOCI カード、製品ビュー:raxiss.com
- 販売店ページ:Tindie — 8BitClub
- 開発スレッド:forum.defence-force.org t=2593
1. ハードウェアの背景
Oric-1 と Atmos(1983 年)は、1 MHz の
MOS 6502 を搭載し、64 KB のアドレス空間のうち上位
($C000-$FFFF) は BASIC ROM に占有されています。ハードウェア乗算はなく、
メモリバンクもなく、現代的なストレージもありません。すべては筐体背面の
拡張バスを経由します。
sodiumlb の LOCI カード(Lovely Oric Computer Interface)は このバスに接続します。技術的には Picocomputer 6502(RP6502)の派生で、 RP2040 マイクロコントローラ(Raspberry Pi Pico)が MIA(Media Interface Adapter)の役割を担い、Oric に対して入出力 周辺機器として現れます。フロッピーディスクドライブとカセットをエミュレートし、SD カードを管理し、 USB ホストとしても機能します。この USB ポートを通じて、Wi-Fi モデム(別体のドングル、 PicoWiFiModemUSB)や HID 周辺機器(マウス、ゲームパッド)が接続されます。Wi-Fi は LOCI カード本体には搭載されていません。ここで注目するアドレス指定面は 2 つです:
Espace d'adressage Oric (64 Ko)
$0000 ┌──────────────────────────────┐
│ RAM │
$0300 ├──────────────────────────────┤
│ VIA 6522 $0300-$030F │
$0310 │ Microdisc WD1793 $0310-$031F │
├──────────────────────────────┤
$0380 │ ACIA 6551 (LOCI) $0380-$0383 │ ← console série / modem
├──────────────────────────────┤
$03A0 │ MIA (LOCI) $03A0-$03BF │ ← fenêtre API 32 octets
├──────────────────────────────┤
$C000 │ ROM BASIC $C000-$FFFF │ ← cible de l'overlay de banque
$FFFF └──────────────────────────────┘
エミュレータ Phosphoric(サイクル精密、C11)はこのカードを忠実に 再現しており、まれな手法が可能になります:拡張の仕様を書き、それをコーディングし、 決定論的なテストで検証する — はんだごてに触れる前に。重要なのは、著者は LOCI カードを所有していますが Oric は持っていない、という点です。ソフトウェアの試験台は贅沢品ではなく、唯一の試験室なのです。
2. fastcall ABI — 6502 がカードを呼び出す仕組み
すべては $03A0-$03BF(MIA)にある 32 個のレジスタウィンドウに
基づいています。ABI が使用する実際のレジスタマップは以下のとおりです(名前はファームウェアに忠実):
Offset Adresse Registre Rôle
------ ------- ---------------- ------------------------------------------
$00 $03A0 CONS_FLAGS bit7 = TX libre, bit6 = RX prêt
$01 $03A1 CONS_TX écriture console (UART)
$02 $03A2 CONS_CHAR lecture console (consomme l'octet)
$0C $03AC API_STACK pointeur de xstack (pile d'arguments)
$0D $03AD API_ERRNO_LO errno bas
$0E $03AE API_ERRNO_HI errno haut
$0F $03AF API_OP ← ÉCRIRE ICI déclenche l'opération
$12 $03B2 BUSY bit7 = carte occupée
$14 $03B4 API_A valeur de retour A
$16 $03B6 API_X valeur de retour X
$18 $03B8 API_SREG retour 16 bits (SREG)
include/io/loci.h より抜粋)。呼び出しプロトコル(fastcall)は 4 段階に収まります:
6502 (Oric) MIA (LOCI, µC)
─────────── ──────────────
1. push args ──► xstack ($03AC)
2. set A/X (paramètres directs)
3. write op ──► API_OP ($03AF) ───► déclenche le handler
├─ BUSY=1
poll BUSY ($03B2) ◄──────────────┤ exécute
└─ BUSY=0, remplit API_A/X/SREG, ERRNO
4. read API_A/API_X ($03B4/$03B6) ◄─ résultat
まだ空いているオペコード値のそれぞれが、新しい機能へのエントリポイントです。
標準操作は $01 から $98 まで(時計、open/read/lseek、
ディレクトリ、イメージのマウント、TAP…)に及びます。未使用のオペコードにこそ、
私たちの 5 つの拡張が収まります:
$A7 SET_BANK banque commutable 16 Ko (--loci-bank)
$A8 STREAM_BANK streamer d'assets (--loci-bank)
$A9 MATH coprocesseur arithmétique (--loci-coproc)
$AA ACIA_RELIABLE mode ACIA fiable (seqlock) (mode opt-in)
+ acia_stat_checked : handshake RX lossless (--loci-acia-rx-nag)
設計による安全装置。対応する有効化フラグがなければ、オペコードは
ENOSYS(errno 13)を返します — パッチされていないファームウェアと全く同じです。 したがって Oric ソフトウェアは拡張の存在を検出し、自前のルーチンに フォールバックできます。LOCI のデフォルト動作は、厳密に元のハードウェアの ままです。
3. 算術コプロセッサ — $A9
問題。6502 には乗算も除算も浮動小数点もハードウェアに備わっていません。 各演算はソフトウェアルーチンであり、遅く、かさばり、サイクルを多く消費します。1 MHz の マシンでは、16×16 の乗算は数百サイクルにも達します。
着想。計算を、はるかに高速なカードのマイクロコントローラに、既存の
fastcall ABI を介して委譲します — 単一のオペコード $A9、演算のサブコードは
API_A に、オペランドは xstack に、結果は
API_A/SREG に。
; exemple conceptuel : A×B via le coprocesseur
LDA #<op_mul : STA API_A ; sous-code d'opération
... push A, B sur la xstack ($03AC)
LDA #$A9 : STA API_OP ; déclenche MATH
; poll BUSY, puis lire le résultat 32 bits dans SREG
実装。独立したファイル
src/io/loci_math.c(op_math)をディスパッチに接続します。
整数、浮動小数点、ベクトル演算。--loci-coproc でゲートされます。
カバレッジ:23 個の決定論的テスト(整数ベクトル、浮動小数点、限界ケース)。
乱数はゼロ — 同じ入力なら同じ出力、これが再現可能な試験台の条件です。
4. 信頼 ACIA モード — $AA(seqlock + ACK)
問題。実際の ACIA 6551 には知られた欠点があります: 6502 がデータレジスタを時間内に読み取らないと、受信したバイトは次のバイトによって 上書きされます。高速時 — 例えば Wi-Fi モデム — にはバイトが失われ、リンクは 使い物にならなくなります。シリコンに忠実ではありますが、足かせになります。
解決策:seqlock。受信シーケンスカウンタと 6502 側のアクノリッジ。 バイトはアクノリッジされて初めて消費されます — 読み取りが 遅れたり失敗したりしても、決して失われません。
Réception classique 6551 (destructive)
─────────────────────────────────────
RX octet1 ──► RDR (6502 n'a pas lu…)
RX octet2 ──► RDR ✗ octet1 ÉCRASÉ, perdu
Mode fiable $AA (seqlock + ACK)
───────────────────────────────
RXSEQ $0384 (compteur, incrémenté à chaque octet présenté)
RXACK $0385 (accusé écrit par le 6502)
RX octet1 ─► présente, RXSEQ++ consommé := (RXACK == RXSEQ)
6502 lit octet1, écrit RXACK = RXSEQ ─► octet1 acquitté → avance
RX octet2 ─► présente seulement si acquitté ✓ aucun octet perdu
送信チャンネルは変更なし(常に信頼できる)。状態は
loci_t(acia_reliable、acia_rx_seq、
acia_rx_presented)が保持し、オペコード $AA、有効化は
API_A の bit0。カバレッジ:+7 テスト
(test-loci-acia-miss、13 → 20)— DATA が非破壊、ACK でゲートされた消費、
複数バイトの seqlock が順序どおり、そして何より読み取り失敗からの
生存。
4bis. ロスレス RX ハンドシェイク — acia_stat_checked(--loci-acia-rx-nag)
実ファームウェア(feature/acia-rx-lossless)に倣った隣接する洗練です。
実機の LOCI では、ACIA の /IRQ はパルスではなくレベル信号です。
このレベルを「nag」でモデル化します:バイトが読み取られない間(stat_checked
が偽)、割り込みは周期的に再発行され(デフォルト:1000 サイクルごと)、
6502 が状態レジスタを参照した途端に沈黙します。
RDRF=1 (octet dispo) ──┐
│ nag: deassert+assert /IRQ tous les 1000 cyc
/IRQ ▁▔▁▔▁▔▁▔▁▔▁▔▁▔▁▔ │ tant que RDRF && !stat_checked && RX-IRQ activée
│
6502 lit STATUS ───────┘ stat_checked = true ──► /IRQ silencieux
--loci-acia-rx-nag がなければ、ACIA は厳密に 6551 のまま
(nag なし)です。test-loci-acia-miss でのカバレッジ:アクノリッジ前に nag が
観測され、後は沈黙し、バッファが空 ⇒ IRQ なし。
5. 16 KB の切替可能バンク — $A7(--loci-bank)
問題。アドレス空間が ROM で飽和しているマシンに、どうやって より多くのメモリを与えるか?
解決策。カードの RAM(xram)の 16 KB を、ROM が居座る
$C000-$FFFF ウィンドウに一時的に重ね合わせます。
$A7 désactivé $A7 EN | SEL=n
$C000 ┌───────────┐ $C000 ┌───────────────┐
│ ROM BASIC │ ─────► │ xram[n*0x4000]│ overlay (lecture)
$FFFF └───────────┘ $FFFF └───────────────┘
└─ ROM intacte DESSOUS (non écrasée)
base xram = SEL * 0x4000 ; SEL clampé à 0..3 (comme mia_set_bank)
要点:非破壊的オーバーレイ。バンクは読み取り(およびメモリ検査)において
優先されますが、ROM 配列を決して上書きしません。無効化すればマシンは
1 バイトずつ復元されます。memcpy + 退避による古いプロトタイプ手法は、
このクリーンな重ね合わせ(memory.c 内の memory_set_loci_bank())の
ために放棄されました。
reset による二重モード。$A7 EN による有効化は
リセットを発生させます:6502 はそのベクタ $FFFC を
バンクから再読み込みします(したがってバンクのコードからブートできます)。
対照的にホットスワップ(ストリーマ $A8 が使用)はCPU リセットなしで
切り替えます — ダブルバッファリングの絶対的な前提条件です。カバレッジ:test-loci
170/170(+4:有効化/状態、無効化、OFF でゲート → ENOSYS、
SEL のクランプ 15→3)と、エンドツーエンドの test-loci-bank-e2e。
6. アセットストリーマ — $A8
着想。バンクを配置したら、そこへファイル(flash または SD カード)
からデータを流し込む — 単一の fastcall で:
lseek(SEEK_SET) + read → 16 KB バンク、任意で
$C000-$FFFF へのマッピング付き。Oric ソフトウェアはこうして
実用 48 KB を超えることができます:オーバーレイ、背景、レベルをオンデマンドで。
Double-buffering (dépasser 48 Ko sans reset)
─────────────────────────────────────────────
$A8 MAP=0 SEL=1 ─► précharge la banque 1 (invisible) ┐ pendant
(le 6502 continue d'exécuter/afficher la banque 0) ┘ ce temps
$A8 MAP=1 SEL=1 ─► bascule banque 1 en $C000 (hot-swap, PC intact)
詳細:API_A bit7 = MAP、bits3:0 = SEL
(0..3 が有効;>3 = EINVAL、$A7 とは異なりクランプなし)。
引数は xstack の LIFO(len, dst, off, fd)、書き込みはバンクサイズに制限され、
戻り値 AX = 読み取りバイト数。読み取り経路は 2 つ:ホストファイル
および SD イメージ。--loci-bank のオプトインを再利用します。
7. tearing モデル — 2 コアという問い
最も微妙で、知的に最も誠実な拡張です。
問い。バス サイクルの進行中にバンクを切り替えると、 6502 は何をラッチするのか?シングルスレッドのエミュレータでは、スワップは アトミックで、目に見えず、この問いは生じません。しかし実際のハードウェアには 2 つのコアがあり、メモリアクセスと同時のスワップは tearing を 生じさせ得ます。
答え:最悪ケースを明示的にモデル化する。--loci-bank-tearing
を用いると、失われた PHI2 バスレースと同時の $A8 MAP ホットスワップが、
ウィンドウの最初の読み取りで 6502 に open-bus をラッチさせ
(ワンショット動作)、その後バンクが引き継ぎます。
Cycle bus PHI2 ─┬─ course gagnée ─► banque servie proprement (atomique)
│
└─ course PERDUE ─► 1re lecture = OPEN-BUS (valeur latchée)
puis ─► banque (one-shot consommé)
(réutilise loci_mia_serve_lost_sampled + jitter seedé, déterministe)
関連するテストは自己診断的です:まず前提条件(レースが
確かに失われていること)を検証し、次にモデルが tearing フラグをアームする
ことを、そしてワンショットが消費されることを検証します。不完全なビルドは、
これまでの謎めいた torn != pat[0] ではなく、明確なアサーションで失敗するようになりました。
決定論的(jitter 0):3 回のクリーンビルドで同一の結果。test-loci-bank-e2e
8/8。
8. この演習が教えること
test-loci スイートを実行 — ヘッドレスの
プロトタイピング試験台。安定ブランチ
(main、166/166) の実際の出力;5 つの拡張は experiment
ブランチで合計を 170 に押し上げます。5 つの拡張、既存の ABI への 5 つの最小限の追加、デフォルト動作への
ゼロ・リグレッション。それぞれが可逆的(フラグに基づく)で、オプトインなしでは
ENOSYS でゲートされ、決定論的なテストに支えられています。
真の教訓はおそらくここにあります:1983 年のマシンにおける難しさは、現代的な機能を 思い描くことではなく、それらを元の動作を裏切ることなく接ぎ木すること — そして はんだごてを取り出す前にそれを証明することにあります。
サイクル精密エミュレータは、単なる遊べる博物館であることをやめ、 ハードウェアのプロトタイピング試験台になります。そこで仕様を書き、拡張を コーディングし、テストを通し… シリコンは最後に、検収書をすでに埋め終えた状態でようやく やって来るのです。
5 つの拡張は Phosphoric の
experiment/loci-coproc-acia-reliable ブランチに存在します — オプトイン、可逆的、
安定版の外。テスト、批評、改善を歓迎します。
出典・リンク
- LOCI ファームウェア/ハードウェア(sodiumlb): loci-firmware、 loci-hardware、 loci-rom
- ユーザーマニュアル(FR): LOCI wiki、 ceo.oric.org
- 販売店・写真: RAXISS、 Tindie
- 開発スレッド: forum.defence-force.org
- エミュレータ Phosphoric: github.com/benedictemarty/Phosphoric、 本サイトの LOCI ドキュメント