首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

VirtuaNES 源碼探究(三):名錶查看器與 PPU 繪圖原理

     編輯:關於C語言

功能入口

工具 → 查看器 → 名錶查看器,彈出 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 色

核心繪圖邏輯:4 個 Name Table 合成

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; // 下一行 
            } 
        } 
    } 
}

關鍵技術點解析

  1. PPU 內存映射:PPU_MEM_BANK[8~11] 對應 4 個 Name Table + Attribute Table(每 1KB)。
  2. Tile 地址計算:Tile 編號 × 16 + 基址選擇位(PPUREG[0] bit4)。高 2 位選 Bank,低 10 位選偏移。
  3. Attribute Table 解析:每 4×4 Tile 共享一字節屬性,通過位運算提取對應 2 位調色板索引。
  4. 位平面圖形解碼:NES 圖案表採用 2bpp 位平面格式,每 Tile 16 字節(8 字節低位 + 8 字節高位),解碼時逐位組合像素值(0~3)再加上屬性偏移(0/4/8/12)。

總結

名錶查看器直觀展示了 NES PPU 如何將 4 個獨立的 Name Table 組合成完整背景圖層,代碼完美還原了硬體邏輯:Tile 索引 → Pattern Table 取圖 → Attribute Table 取色 → 位平面解碼 → 畫布合成。本文出自「三人乘虎」博客:原文鏈接

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved