啟動NES模擬器,再一次打開我們經典的超級馬裡奧1。
選擇工具->查看器->卷軸查看器。這次會出現如下的一個窗口。
響應函數依舊是
WNDCMD CMainFrame::OnViewCommand( WNDCMDPARAM )
這方面的內容上節說過就不說了。
這次進入的是第二個分支
case ID_VIEW_NAMETABLE: if( !m_NameTableView.m_hWnd ) { m_NameTableView.Create( HWND_DESKTOP ); } ::SetWindowPos( m_NameTableView.m_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE ); break;
直接介紹m_NameTableView所屬的類。
CNameTableView
所在文件 Source Files/NameTableView.cpp Header Files/NameTableView.h
可以看到CNameTableView和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; m_BitmapHdr.bih.biCompression = BI_RGB; m_BitmapHdr.bih.biClrUsed = 16;
可以知道顯示在窗口上的位圖寬512像素,高480像素。
512*480有什麼特殊含義嗎?有!
NES中有4個命名表和屬性表的組合。1個組合就可以在屏幕上顯示出一幅畫面了。
試著將512*480除以4,得到256*240。NES游戲畫面的分辨率正好是256*24032*30個tile組成畫面,tile是8*8像素的,由此求得)。
這樣就可以推斷出,顯示在卷軸查看器上的是4幅游戲背景。
至於NES中為何有4個命名表和屬性表的組合,這暫時還不必深入研究其實是博主我自己也還不懂)。
接下來深入講解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]; LPBYTE lpScnv = &m_lpPattern[(n>>1)*512*240+(n&1)*256]; for( INT y = 0; y < 30; y++ ) { for( INT x = 0; x < 32; x++ ) { INT tile = lpVRAM[x+y*32]*16+((PPUREG[0]&PPU_BGTBL_BIT)<<8); 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]; for( INT p = 0; p < 8; p++ ) { BYTE chr_l = lpPtn[p]; BYTE chr_h = lpPtn[p+8]; 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; // Next line lpScn+=512; } } } }
第1行 從左到右依次畫4幅游戲背景。
第2行 上節已經講過PPU_MEM_BANK的後四個字節指針是指向四個命名表和屬性表。因此lpVRAM指向第n個命名表和屬 性表。
第3行 第n幅背景的首像素的地址。
第5-6行 30,32兩個數字大家不陌生了吧,分別是縱向和橫向的tile數。
第7-8行 如果把PPU_MEM_BANK看成大小為12*1K的連續空間在真機,也就是FC家用機中,這塊地址空間就是連續的),那麼這個式子求出的結果就是tile的地址。不過由於這裡把地址空間給分成了12份,因此求得的值10~13位是PPU_MEM_BANK數組的下標,0~9位是1K空間中的偏移量。
式子每部分的含義,再介紹一下。
lpVRAM[x+y*32]是tile的編號。一個tile在圖案表中占16個字節,因此lpVRAM[x+y*32]*16是該tile在圖案表中的偏移地址。
PPUREG[0]是一個IO端口,長16位,每一位都有特定含義。具體每一位什麼含義可先不管,只需知道第4位表示當前使用的圖案表的地址。該位為0,使用0x0000的圖案表,該位為1,使用0x1000的圖案表。(PPUREG[0]&PPU_BGTBL_BIT)<<8這個式子就是用來求得0x0000或0x1000。
第9-10行 求得的是每一個tile對應的高兩位數據。attr第2,3位存放求得的數據,其余位為0之所以這麼保存,是為了後面求算方便)。
第11行 tile的起始屏幕指針。
第12行 從圖案表讀取1個tile所有像素的低2位,共16個字節。
第13行 正式開始畫tile,每次循環畫1行。
第14-15行 之前提到過,2個字節,第1個字節是像素的第0位,第2個字節是像素的第1位。
第17-24行 在求每一個像素。有了前面的基礎,這些個式子應該很容易看懂。
第26行 指向tile的下一行的起始位置。
本文出自 “三人乘虎” 博客,請務必保留此出處http://darhx.blog.51cto.com/7920146/1303236