工具 → 查看器 → 名錶查看器,彈出 512×480 像素窗口,顯示 4 個 256×240 的背景畫面(NES 有 4 個 Name Table + Attribute Table 組合)。
CNameTableView繼承自 CScrollView,與圖案查看器 CPatternView 結構相似,關鍵差異在繪圖邏輯。
m_BitmapHdr.bih.biSize = sizeof(BITMAPINFOHEADER); m_BitmapHdr.bih.biWidth = 512; m_BitmapHdr.bih.biHeight = -480; // 負值表示自上而下 m_BitmapHdr.bih.biPlanes = 1; m_BitmapHdr.bih.biBitCount = 8; // 256 色調色板 m_BitmapHdr.bih.biCompression = BI_RGB; m_BitmapHdr.bih.biClrUsed = 16; // 僅使用前 16 色
OnTimer 週期性更新)for (INT i = 0; i < 16; ++i)
m_BitmapHdr.rgb[i] = m_Palette[BGPAL[i]]; // 讀取背景 16 色
for (INT n = 0; n < 4; ++n) {
LPBYTE lpVRAM = PPU_MEM_BANK[8 + n]; // 第 n 個 Name Table 基址
LPBYTE lpScnv = &m_lpPattern[(n >> 1) * 512 * 240 + (n & 1) * 256]; // 目標畫布偏移
for (INT y = 0; y < 30; ++y) { // 30 行 Tile
for (INT x = 0; x < 32; ++x) { // 32 列 Tile
// 1. 計算 Tile 在 Pattern Table 中的地址
INT tile = lpVRAM[x + y * 32] * 16 + ((PPUREG[0] & PPU_BGTBL_BIT) << 8);
// 2. 讀取 Attribute Table 高 2 位調色板索引
BYTE attr = ((lpVRAM[0x03C0 + (x/4) + (y>>2)*8] >> ((x&2) + (y&2)*2)) & 3) << 2;
LPBYTE lpScn = &lpScnv[x*8 + y*8*512]; // 畫布寫入起點
LPBYTE lpPtn = &PPU_MEM_BANK[tile >> 10][tile & 0x03FF]; // Pattern Table 數據
// 3. 逐行解碼 8×8 像素
for (INT p = 0; p < 8; ++p) {
BYTE chr_l = lpPtn[p];
BYTE chr_h = lpPtn[p + 8];
// 位平面解碼:高位在 chr_h,低位在 chr_l
lpScn[0] = ((chr_h >> 6) & 2) | ((chr_l >> 7) & 1) | attr;
lpScn[4] = ((chr_h >> 2) & 2) | ((chr_l >> 3) & 1) | attr;
lpScn[1] = ((chr_h >> 5) & 2) | ((chr_l >> 6) & 1) | attr;
lpScn[5] = ((chr_h >> 1) & 2) | ((chr_l >> 2) & 1) | attr;
lpScn[2] = ((chr_h >> 4) & 2) | ((chr_l >> 5) & 1) | attr;
lpScn[6] = ((chr_h >> 0) & 2) | ((chr_l >> 1) & 1) | attr;
lpScn[3] = ((chr_h >> 3) & 2) | ((chr_l >> 4) & 1) | attr;
lpScn[7] = ((chr_h << 1) & 2) | ((chr_l >> 0) & 1) | attr;
lpScn += 512; // 下一行
}
}
}
}
PPU_MEM_BANK[8~11] 對應 4 個 Name Table + Attribute Table(每 1KB)。PPUREG[0] bit4)。高 2 位選 Bank,低 10 位選偏移。名錶查看器直觀展示了 NES PPU 如何將 4 個獨立的 Name Table 組合成完整背景圖層,代碼完美還原了硬體邏輯:Tile 索引 → Pattern Table 取圖 → Attribute Table 取色 → 位平面解碼 → 畫布合成。本文出自「三人乘虎」博客:原文鏈接