【譯制】紅白機(jī)NTSC制式PPU——Ricoh 2C02技術(shù)參考(第二部分)
接《【譯制】紅白機(jī)NTSC制式PPU——Ricoh 2C02技術(shù)參考(第一部分)》

?掃描線渲染細(xì)節(jié)
PPU在一條可見掃描線中會從VRAM的命名表、屬性表和樣式表中讀取數(shù)據(jù),用來產(chǎn)生屏幕上的圖像。這一部分將詳細(xì)闡述PPU在一條可見掃描線周期內(nèi)的行為。
?
正如前文所述,每一次VRAM訪問花費(fèi)兩個PPU時鐘周期。而每條掃描線長度為341個PPU時鐘周期,在此期間PPU能完成170次VRAM訪問(并且用盡這170次VRAM訪問)。在從VRAM取回170次數(shù)據(jù)后,PPU會有一個時鐘周期的空閑期。別忘了PPU在每一個時鐘周期都會渲染一個像素。

第1-128次VRAM訪問的每一次
1. 讀一個字節(jié)的命名表數(shù)據(jù)
2. 讀一個字節(jié)的屬性表數(shù)據(jù)
3. 讀對應(yīng)樣式表位圖的0號字節(jié)
4. 讀對應(yīng)樣式表位圖的1號字節(jié)
?
這一過程會重復(fù)32次(每一條掃描線上有32個圖塊,每個圖塊每一行有8個像素,而NES/FC的分辨率為256*240)。
?
在此期間PPU會從VRAM中取回對應(yīng)的數(shù)據(jù)并渲染背景。此掃描線中第一個被取回的背景圖塊被作為屏幕上該行第三個圖塊顯示(屏幕上該行的前兩個背景圖塊的數(shù)據(jù)是在上一條掃描線的最后被取回的)。
?
屏幕上所有顯示像素的數(shù)據(jù)都是在這256個PPU時鐘周期(128個VRAM訪問周期)里被輸出到PPU視頻輸出引腳。從圖塊位圖(一行,8位即1個字節(jié))開始被取回(即上述四次完整內(nèi)存訪問的開始)到此圖塊位圖數(shù)據(jù)的第一個像素被輸出到視頻輸出引腳,一共經(jīng)歷了(16-n)個PPU時鐘周期,其中n代表屏幕水平卷動偏移的低3位(譯者注:PPU屏幕水平和垂直卷動偏移各8位,單位為像素,即卷動范圍為0-255,而低3位表示的卷動范圍為0-7,因為一個圖塊有水平方向有8個像素,因而0-7表示“圖塊內(nèi)的平滑卷動”;而無圖塊內(nèi)水平卷動偏移時所經(jīng)歷的16個時鐘周期中,2*4個時鐘周期用于訪問四次VRAM,另外8個用于下文所述的背景渲染流水線移位寄存器的移位操作,這就是(16-n)的由來,16個時鐘周期,恰好有16個像素被輸出,為2個圖塊的寬度)。此信息對于理解基于“0號精靈碰撞”標(biāo)志的掃描線精確計時操作至關(guān)重要。
?
我們注意到PPU每次取回的屬性表數(shù)據(jù)作用于其繪制的8個連續(xù)的水平像素(一個圖塊的一行)。這制約了PPU的同屏發(fā)色數(shù)(也就是說此區(qū)域的像素只能共用1個只有3種顏色的用戶調(diào)色板項,更準(zhǔn)確的說,一個由2*2個圖塊(Tile)構(gòu)成的區(qū)塊(Block)需要共用一個用戶調(diào)色板項),也就是說8個連續(xù)的水平像素位于同一個“顏色區(qū)域”中(屬性表為該區(qū)域選擇了某一個用戶調(diào)色板)。
?
在此期間PPU還會將下一條掃描線的縱坐標(biāo)(Y)與OAM中64個精靈的表項逐一比較,來判斷是否有精靈需要在下一條掃描線上繪制(優(yōu)先于/覆蓋背景像素),即判斷是否有精靈落入了下一條可見掃描線的范圍之中(這正是OAM表項中的精靈縱坐標(biāo)是(其出現(xiàn)的掃描線-1)的原因)。每一次比較耗費(fèi)四個PPU時鐘周期(據(jù)猜測),因此64個精靈花費(fèi)256個時鐘周期(這恰好與屏幕上像素的渲染過程同步進(jìn)行)。

畫面內(nèi)精靈的判定
PPU內(nèi)部有一個用來計算(當(dāng)前掃描線-21)和OAM每個精靈表項的(縱坐標(biāo)-1)差值的8位比較器,結(jié)果是一個9位二進(jìn)制數(shù)。如果比較得到的差值在0-7($2000 寄存器的第五位等于0,使用矮精靈)或0-15($2000 寄存器的第五位等于1,使用高精靈)的范圍內(nèi),則表明精靈在被比較的掃描線內(nèi)。
?
(值得注意的是比較結(jié)果是一個9位二進(jìn)制數(shù)。也就是說如果將精靈掃描線坐標(biāo)設(shè)置為(-1)-(-15),比較器會將其視為241-255處理,這是8位坐標(biāo)值和9位結(jié)果的符號位的差異造成的。在這種情況下,精靈將不在任何一條可見掃描線的范圍之內(nèi),也就表明我們無法實(shí)現(xiàn)精靈從屏幕頂端平滑地向上卷動出屏幕的效果)
?
PPU內(nèi)部存放的該掃描線內(nèi)精靈的OAM表項中的8位的圖塊索引、8位的X坐標(biāo)、4位的屬性信息,以及比較得到差值的低四位(精靈內(nèi)垂直偏移)共同構(gòu)成了“精靈/OAM臨時內(nèi)存”?(不考慮垂直翻轉(zhuǎn)的情況)。如果該精靈在其OAM表項中設(shè)置了垂直翻轉(zhuǎn),先前得到的4位的比較差值會被做邏輯非運(yùn)算。
?
因為以上精靈范圍的判定過程是順序遍歷整個OAM內(nèi)存的(從第0個到第63個精靈),所以OAM臨時內(nèi)存會按照精靈優(yōu)先級從高到低的順序追加更新(若需要)。PPU中一個4位的“掃描線上精靈”計數(shù)器用于記錄掃描線上發(fā)現(xiàn)的精靈個數(shù)(0-8個),同時也被用作OAM臨時內(nèi)存的索引指針,來將新發(fā)現(xiàn)的精靈數(shù)據(jù)放入最多容納8個元素(精靈)的OAM臨時內(nèi)存。在精靈判定階段的開始,該計數(shù)器被清零,并在每發(fā)現(xiàn)一個掃描線上的精靈后自增1。在該計數(shù)器的值為8之前,該過程持續(xù)進(jìn)行,此后發(fā)現(xiàn)的精靈將被丟棄,同時?$2002 寄存器的第5位被置位(精靈溢出),以指示下一條掃描線上將有精靈不被顯示。
?
OAM臨時內(nèi)存還帶有一個額外的比特位,用來指示相應(yīng)掃描線上是否存在0號精靈(優(yōu)先級最高的精靈)。這一信息將被用于后續(xù)的0號精靈與非0背景像素碰撞的判斷。

背景渲染流水線的細(xì)節(jié)
在樣式表數(shù)據(jù)和用戶調(diào)色板選擇數(shù)據(jù)(使用一個兩位信號控制的四選一選擇器從取回的一個字節(jié)屬性表數(shù)據(jù)中選出兩位作為用戶調(diào)色板選擇數(shù)據(jù),如何選擇與當(dāng)前圖塊在2*2圖塊構(gòu)成的區(qū)塊中的位置有關(guān))被取回后,它們將被載入PPU內(nèi)部的鎖存器。
(譯者注:一個圖塊某一行8個像素的用戶調(diào)色板的顏色選擇信號由被命名表數(shù)據(jù)選出的樣式表數(shù)據(jù)連續(xù)兩個字節(jié)拼湊而成,單個像素的用戶調(diào)色板顏色選擇信號有兩位,通過一個四選一選擇器選中上述“用戶調(diào)色板選擇數(shù)據(jù)”選出的某個用戶調(diào)色板中四種系統(tǒng)調(diào)色板索引的某一個,作為硬編碼的系統(tǒng)調(diào)色板的選擇信號(8位,實(shí)際6位有效),該信號編碼了最終的顏色,詳見“視頻信號的產(chǎn)生”)。
?
在新圖塊數(shù)據(jù)取回階段(8個PPU時鐘周期)的開始,上一次取回并鎖存的2個字節(jié)圖塊樣式表位圖被分別載入2個16位移位寄存器的高八位(這兩個移位寄存器會在每個時鐘周期右移一次)。在此期間,2位用戶調(diào)色板選擇數(shù)據(jù)也會被傳送至另一個鎖存器(這個2位的鎖存器的2個比特位將分別作為另外2個在每個時鐘周期右移一次的8位右移寄存器的串行輸入)。4位的像素數(shù)據(jù)(2位用戶調(diào)色板選擇數(shù)據(jù)和2位用戶調(diào)色板顏色選擇數(shù)據(jù),即唯一的4位用戶調(diào)色板顏色索引)被拆分灌入這4個額外的移位寄存器,目的是在PPU固定的訪問VRAM按字節(jié)取回圖塊數(shù)據(jù)的工作方式下實(shí)現(xiàn)平滑的水平卷動。
?
最終將從每個移位寄存器中各選出一個比特位,形成當(dāng)前時鐘周期有效的4位背景像素值(用戶調(diào)色板的顏色索引)。從4個移位寄存器中選出比特位時使用的偏移/索引是基于用戶設(shè)置的水平屏幕卷動偏移產(chǎn)生的(正是8位水平偏移的低3為,表示圖塊內(nèi)偏移,此索引范圍為0-7,即選擇4個移位寄存器的0-7位)。選出的4位像素數(shù)據(jù)會被送入多路復(fù)用器(下文描述),與精靈數(shù)據(jù)進(jìn)行混合(選擇)后輸出。

第129-160次VRAM訪問的每一次
1. 讀一個字節(jié)的廢棄命名表數(shù)據(jù)
2. 讀一個字節(jié)的廢棄屬性表數(shù)據(jù)
3. 讀下一條掃描線上相應(yīng)精靈(如果有,對應(yīng)OAM臨時內(nèi)存索引0-7)樣式表位圖的0號字節(jié)
4. 讀下一條掃描線上相應(yīng)精靈(如果有,對應(yīng)OAM臨時內(nèi)存索引0-7)樣式表位圖的1號字節(jié)
?
以上過程重復(fù)8次(因為一條掃描線上最多能顯示8個精靈)。
?
這個階段留給PPU取回下一條掃描線上需要顯示精靈的樣式表數(shù)據(jù)。當(dāng)(“掃描線上精靈”計數(shù)器所指示的)下一條掃描線上不足8個精靈時,取回的樣式表數(shù)據(jù)將來自“啞樣式表”而不是實(shí)際有用的樣式表。此時在PPU內(nèi)部,取回的“啞數(shù)據(jù)”將會被丟棄,并使用完全透明的樣式表位圖代替。
?
雖然取回的命名表數(shù)據(jù)最終被丟棄,且使用到的命名表地址某種程度上是不可預(yù)知的,但是這個命名表地址似乎與下一條掃描線上的第一個圖塊的命名表有關(guān)。這似乎暗示著在掃描線第256個時鐘周期(第128次VRAM訪問結(jié)束時),PPU的卷動/地址計數(shù)器的值將被自動重裝載為PPU水平屏幕卷動寄存器的值(譯者注:也就是說一條掃描線中的水平卷動偏移保持一致,依照水平卷動計數(shù)器的值來設(shè)置水平方向的偏移,在此期間若CPU執(zhí)行的程序改變了PPU水平屏幕卷動寄存器的值,該水平卷動的變化將反映在下一條掃描線上)。
?
同樣值得關(guān)注的是,因為下一條掃描線的精靈樣式表需要在本條掃描線取回,所以第一條可見掃描線前的“啞掃描線”是必要的,只有這樣,第一條可見掃描線上的精靈才能被判定,OAM臨時內(nèi)存的條目才可以被初始化,第一條掃描線上精靈的樣式位圖信息才可以被取出。
?
至于為什么要在取回精靈樣式前取回廢棄的命名表和屬性表數(shù)據(jù),大概是因為任天堂想在精靈渲染的硬件上復(fù)用背景樣式表取回的硬件才選擇這么做的吧。

?精靈樣式的取出和渲染細(xì)節(jié)
PPU從VRAM的何處取回單個精靈的樣式表數(shù)據(jù)受到OAM臨時內(nèi)存表項和?$2000 寄存器的第五位(精靈高度選擇)的控制。如果?$2000 寄存器的第五位為0,PPU使用一般的圖塊索引,使用到的樣式表由$2000 寄存器的第三位選擇。而如果?$2000 寄存器的第五位為1(使用16線高精靈),由地址計算電路產(chǎn)生的樣式表內(nèi)偏移的高位比特將被作為圖塊索引號的低位比特,而OAM中存放的精靈圖塊索引的最低位將被作為VRAM低端兩個樣式表(也稱圖案表)的選擇位。地址計算電路產(chǎn)生VRAM地址的最低三位則永遠(yuǎn)會被作為精靈樣式位圖內(nèi)部的垂直偏移量。
?
若相應(yīng)的OAM臨時內(nèi)存表項指示當(dāng)前精靈設(shè)置了水平翻轉(zhuǎn),則取回的一字節(jié)樣式表位圖數(shù)據(jù)的比特位將會依次發(fā)生交換以實(shí)現(xiàn)精靈的水平翻轉(zhuǎn)。
?
取回的兩個字節(jié)的樣式表數(shù)據(jù),外加OAM中的3位精靈屬性信息(兩位調(diào)色板選擇位和一位精靈相對于背景像素的優(yōu)先級位)和一個字節(jié)的精靈橫坐標(biāo)被載入PPU中一個叫作“精靈緩沖內(nèi)存”的部分(0號精靈指示位也一同被拷貝)。這一片存儲區(qū)同樣能存放8個精靈的數(shù)據(jù)。
?
精靈緩沖內(nèi)存的每一個元素都是由以下部分組成的:2個8位移位寄存器(取回的兩個字節(jié)樣式位圖數(shù)據(jù)將分別載入進(jìn)兩個移位寄存器中,并在合適的時間里移位并移出)、1個3位鎖存器(存放精靈的用戶調(diào)色板選擇和相對于背景的優(yōu)先級信息)和1個8位減1計數(shù)器(精靈的橫坐標(biāo)被載入到這里)。
?
上述的8位計數(shù)器會在PPU每渲染一個像素時自減1(在掃描線的前256個時鐘周期內(nèi)進(jìn)行,詳見上文的“第1-128次VRAM訪問的每一次”)。當(dāng)計數(shù)器的值等于0時,上面提到的兩個存放樣式表數(shù)據(jù)的移位寄存器會開始移位,并在每個時鐘周期移位一次。在計數(shù)器自減至0之前及8次移位(有效數(shù)據(jù)被全部移出)之后,移位寄存器的串行輸出端會始終輸出0(代表透明)。
?
8個精靈樣式位圖的串行移位輸出是需要經(jīng)過優(yōu)先級裁定的,最終只會有一條串行移位輸出(連同該精靈的用戶調(diào)色板選擇和相對于背景的優(yōu)先級位)被選出并輸出到多路復(fù)用器(在這里進(jìn)行精靈和背景像素的優(yōu)先級裁定)。
?
如果從精靈緩沖內(nèi)存第一個條目(元素)移出的像素(樣式表位圖比特)是非透明(非0)的,則其中的數(shù)據(jù)(包括0號精靈標(biāo)志)則會被最優(yōu)先地送入多路復(fù)用器并停止精靈優(yōu)先級的裁決過程。否則優(yōu)先權(quán)將被交給精靈緩沖內(nèi)存的下一個元素,重新測試其輸出的精靈像素透明與否(而當(dāng)前的0號精靈標(biāo)志將會被一直傳遞下去,當(dāng)前是“無0號精靈”的狀態(tài))。若前7個精靈均輸出透明像素,則第8個精靈的數(shù)據(jù)將被無條件地傳遞給多路復(fù)用器。注意,這個過程在每個時鐘周期都會發(fā)生(硬件會以電信號在邏輯電路中傳播的速度迅速裁決出優(yōu)先級,此時間短于一個時鐘周期)。

多路復(fù)用器的行為
多路復(fù)用器有兩件事要做:判斷0號精靈與非透明(非0)背景是否發(fā)生了碰撞,并在裁決背景像素和最高優(yōu)先級的非透明精靈像素的優(yōu)先級后將優(yōu)先級高者的像素數(shù)據(jù)傳遞給用戶調(diào)色板。
?
“0號精靈碰撞”事件在非透明背景像素和非透明精靈像素在多路復(fù)用器相遇,且當(dāng)前時鐘周期精靈優(yōu)先級裁定電路傳給多路復(fù)用器的0號精靈標(biāo)志被置位的條件下產(chǎn)生。這將導(dǎo)致?$2002 寄存器的第六位對應(yīng)的觸發(fā)器被置位,這個標(biāo)志將一直保持,并在下一幀開始渲染時被清除。
?
選出需要傳遞給用戶調(diào)色板的像素的過程非常簡單。使用精靈數(shù)據(jù)而不是背景數(shù)據(jù)來產(chǎn)生最終輸出到屏幕上的像素的充要條件是:
?
(精靈的優(yōu)先級比背景高?或?背景像素是透明的)?且?精靈像素不是透明的
?
PPU有兩個用戶調(diào)色板:一個精靈調(diào)色板和一個背景調(diào)色板。因此,最終的調(diào)色板索引數(shù)據(jù)(即上文所謂的“像素”)被傳遞給哪個調(diào)色板以索引得到硬編碼系統(tǒng)調(diào)色板的顏色,取決于最終通過多路選擇器的是精靈像素還是背景像素。
?
再經(jīng)過用戶調(diào)色板的索引后,PPU便會以上文“視頻信號的產(chǎn)生”一節(jié)中描述的過程輸出視頻信號。

第161-168次VRAM訪問的每一次
1. 讀一個字節(jié)的命名表數(shù)據(jù)(對應(yīng)下一條掃描線上的連續(xù)8個像素)
2. 讀一個字節(jié)的屬性表數(shù)據(jù)(對應(yīng)下一條掃描線上的連續(xù)8個像素)
3. 讀對應(yīng)樣式表位圖的0號字節(jié)
4. 讀對應(yīng)樣式表位圖的1號字節(jié)
?
這一過程將重復(fù)2次。
?
在此期間,PPU從VRAM取回下一條掃描線上第1和第2個背景圖塊數(shù)據(jù)。PPU會用取回的數(shù)據(jù)有效位圖數(shù)據(jù)初始化內(nèi)部背景像素流水線(準(zhǔn)確地說是那2個16位的移位寄存器)。下一條掃描線上剩下的背景圖塊(第3至32個背景圖塊)將會在下一條掃描線的開始被取回并渲染。

第169-170次VRAM訪問的每一次
1. 讀一個字節(jié)的命名表數(shù)據(jù)
2. 讀一個字節(jié)的命名表數(shù)據(jù)
?
我不太清楚這兩次VRAM訪問的目的何在。這里兩次顯存中命名表的訪問所使用到的地址,均指向屏幕上要渲染的第3個圖塊(即下一條掃描時間中渲染的第1個圖塊或下一條掃描線上實(shí)際顯示的第3個圖塊的命名表地址)。

第170次VRAM訪問之后
PPU在重復(fù)以上步驟進(jìn)行下一條掃描線的計時之前會空閑1個時鐘周期(即半個VRAM訪問周期)。

(未完待續(xù))