← 返回測試報告

AprNes 測試方法論

透過硬體測試 ROM 驗證 NES 模擬器精確度

174
測試 ROM
30+
測試套件
5
子系統
<3 分鐘
完整執行

1. 測試內容

AprNes 使用硬體驗證測試 ROM,這些 ROM 最初是為了驗證真實 NES 主機行為而編寫的。 它們是實際的 NES 程式(6502 機器碼),用於測試特定硬體功能並回報通過/失敗結果。 所有主流 NES 模擬器專案都使用相同的 ROM 來衡量精確度。

測試涵蓋所有主要 NES 子系統:

2. 測試 ROM 來源

所有 ROM 來自 nes-test-roms 合集,主要作者包括:

這些 ROM 與 Mesen、Nestopia、FCEUX 及其他參考模擬器使用的完全相同。通過這些測試代表模擬已達到週期精確或接近週期精確的程度。

3. 測試執行器架構

Bash 腳本
run_tests_report.sh
MSBuild
編譯模擬器
無頭模擬器
TestRunner.cs
結果偵測
$6000 / 畫面掃描
HTML 報告
JSON + 截圖

無頭模式

模擬器內建 TestRunner.cs,以無頭模式運行 — 沒有視窗、沒有音效、沒有幀率限制。CPU/PPU/APU 全部以最高速度運行。單個測試 ROM 通常在 1 秒內完成。

調度腳本

一個 bash 腳本(run_tests_report.sh)處理完整的流程:

  1. 使用 MSBuild 編譯專案
  2. 將 174 個 ROM 檔案逐一透過無頭模擬器執行
  3. 擷取最終畫面為 PNG,轉換為無損 WebP
  4. 將結果收集為 JSON 陣列
  5. 產生包含嵌入資料的單一 HTML 報告檔案

4. 結果偵測機制

機制 A:$6000 記憶體協定

現代 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」。

機制 B:畫面穩定偵測

較舊的 blargg 測試(2005 年版)不使用 $6000 協定。它們將結果直接渲染到 PPU 名稱表。測試執行器透過多步驟啟發式方法處理這些情況:

  1. 在 120 幀(約 2 秒)後,開始每幀取樣畫面緩衝區
  2. 計算幀緩衝區的雜湊值(每隔 37 個像素取樣以提高速度)
  3. 當雜湊值連續 90 幀(約 1.5 秒)保持一致時,畫面判定為「穩定」
  4. 掃描 PPU 名稱表(字元映射)尋找已知結果字串:
    • "Passed" / "PASSED" → 通過
    • "Failed" / "FAILED" → 失敗
    • "$01"(畫面上的十六進位值)→ 通過
    • "$02" ~ "$FF"(畫面上的十六進位值)→ 失敗
    • "All tests complete" → 通過
    • " 0/"(零錯誤計數)→ 通過

此方法直接讀取 PPU 名稱表(而非對像素進行 OCR),因此快速且可靠。

5. 自動化功能

自動軟重設

部分測試 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 進入無限迴圈或當機,執行器會優雅地終止它並回報最後已知狀態。

6. 測試套件涵蓋範圍

4apu_mixer — 聲道混音
6apu_reset — APU 開機/重設
9apu_test — APU 影格計數器
11blargg_apu_2005 — APU 時序
2blargg_cpu_test5 — CPU 指令
5blargg_ppu_tests — PPU 基礎
3branch_timing — 分支週期計數
1cpu_dummy_reads — 虛擬讀取週期
2cpu_dummy_writes — 虛擬寫入週期
2cpu_exec_space — 從 I/O 執行
6cpu_interrupts_v2 — NMI/IRQ 互動
2cpu_reset — CPU 重設行為
1cpu_timing_test6 — 指令時序
5dmc_dma_during_read — DMC DMA 衝突
5instr_misc — 雜項指令測試
17instr_test-v3 — 全部 6502 指令
18instr_test-v5 — 全部 6502 指令(v5)
3instr_timing — 指令週期時序
6mmc3_irq_tests — MMC3 IRQ 計數器
6mmc3_test — MMC3 行為
6mmc3_test_2 — MMC3 行為(v2)
11nes_instr_test — CPU 指令(替代版)
1oam_read — OAM 讀取行為
1ppu_open_bus — PPU 開放匯流排
1ppu_read_buffer — PPU 讀取緩衝區
11ppu_vbl_nmi — VBlank/NMI 時序
4read_joy3 — 控制器讀取
2sprdma_and_dmc_dma — DMA 衝突
11sprite_hit_tests — 精靈 0 命中
5sprite_overflow — 精靈溢位
7vbl_nmi_timing — VBL/NMI 時序

7. 命令列介面

無頭測試執行器透過模擬器執行檔直接呼叫:

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 = 逾時/無結果。