透過硬體測試 ROM 驗證 NES 模擬器精確度
AprNes 使用硬體驗證測試 ROM,這些 ROM 最初是為了驗證真實 NES 主機行為而編寫的。 它們是實際的 NES 程式(6502 機器碼),用於測試特定硬體功能並回報通過/失敗結果。 所有主流 NES 模擬器專案都使用相同的 ROM 來衡量精確度。
測試涵蓋所有主要 NES 子系統:
所有 ROM 來自 nes-test-roms 合集,主要作者包括:
這些 ROM 與 Mesen、Nestopia、FCEUX 及其他參考模擬器使用的完全相同。通過這些測試代表模擬已達到週期精確或接近週期精確的程度。
模擬器內建 TestRunner.cs,以無頭模式運行 — 沒有視窗、沒有音效、沒有幀率限制。CPU/PPU/APU 全部以最高速度運行。單個測試 ROM 通常在 1 秒內完成。
一個 bash 腳本(run_tests_report.sh)處理完整的流程:
現代 blargg 測試 ROM 使用記憶體映射狀態協定。測試執行器每幀輪詢位址 $6000:
| $6000 值 | 意義 | 動作 |
|---|---|---|
$80 | 測試進行中 | 繼續等待 |
$81 | 要求重設 | 等待 100ms 後執行軟重設 |
$00 | 測試通過 | 以代碼 0 結束 |
$01-$7F | 測試失敗(錯誤代碼 N) | 以代碼 N 結束 |
結果文字從 $6004+ 讀取,為以 null 結尾的 ASCII 字串。這提供了詳細的錯誤訊息,例如「Flag first set too late」或「Length counter not clocked correctly」。
較舊的 blargg 測試(2005 年版)不使用 $6000 協定。它們將結果直接渲染到 PPU 名稱表。測試執行器透過多步驟啟發式方法處理這些情況:
"Passed" / "PASSED" → 通過"Failed" / "FAILED" → 失敗"$01"(畫面上的十六進位值)→ 通過"$02" ~ "$FF"(畫面上的十六進位值)→ 失敗"All tests complete" → 通過" 0/"(零錯誤計數)→ 通過此方法直接讀取 PPU 名稱表(而非對像素進行 OCR),因此快速且可靠。
部分測試 ROM 會寫入 $81 到 $6000 以要求主機重設(測試開機/重設行為)。執行器偵測到此情況後,會在延遲 100ms 後自動執行軟重設,模擬使用者按下重設按鈕的操作。每個 ROM 最多支援 10 次連續重設。
控制器讀取測試需要實際的按鈕輸入。--input 參數可排程定時按鈕事件:
--input "A:2.0,B:4.0,Select:6.0,Start:8.0,Up:10.0,Down:12.0,Left:14.0,Right:16.0"
每個按鈕在指定時間(秒)被按下,並持續 10 幀(約 166ms)。這使得 read_joy3/test_buttons 等測試能驗證所有 8 個按鈕是否被正確依序偵測。
每個測試的最終畫面會被擷取為 256x240 PNG,然後轉換為無損 WebP(通常縮小 60-80%)。截圖作為視覺佐證 — 許多測試 ROM 會在畫面上以文字顯示結果,確切展示通過或失敗的項目。
每個 ROM 有可設定的 --max-wait 逾時時間(預設 30 秒,較長測試為 120 秒)。若測試 ROM 進入無限迴圈或當機,執行器會優雅地終止它並回報最後已知狀態。
無頭測試執行器透過模擬器執行檔直接呼叫:
AprNes.exe --rom <file.nes> [選項]
| 選項 | 說明 |
|---|---|
--rom <path> | 要載入的 ROM 檔案(必填) |
--wait-result | 監控 $6000 / 畫面以偵測測試結果 |
--max-wait <sec> | 逾時秒數(預設:30) |
--time <sec> | 精確執行 N 秒後停止 |
--screenshot <path> | 將最終畫面儲存為 PNG |
--log <path> | 將結果寫入檔案 |
--soft-reset <sec> | 在第 N 秒觸發軟重設 |
--input <spec> | 排程按鈕輸入(例如 "A:2.0,B:4.0") |
--debug-log <path> | 寫入 CPU 追蹤日誌 |
結束代碼:0 = 通過、1-127 = 失敗(測試錯誤代碼)、255 = 逾時/無結果。