引言
CPU 要執行指令需要先識別指令,弄清楚要執行的指令是什麼型別、需要幾個週期、運算元在哪裡、目的地在哪裡等資訊,才能在後續的指令執行過程中開啟對應的資料通路。“識別指令”的過程叫譯碼,完成指令識別功能的機構,叫譯碼器。
兩個譯碼器
因為 6502 CPU 有一個兩級流水線,所以有兩個譯碼器,分別叫前置譯碼器(Pre-Decoder)和後置譯碼器。通常說的 6502 譯碼器實際是指後置譯碼器。這兩個譯碼器在電路圖中位置如下圖:
圖中,紫色表示前置譯碼器,藍色表示後置譯碼器。我沒有在 6502 Schematics.pdf 檔案中找到前置譯碼器,可能是作者沒有整理出來。
兩個譯碼器之間的協作關係可以從 Hanson's Block Diagram 中看出來:
可以看到,前置譯碼器在指令暫存器(IR)之前,而後置譯碼器在 IR 之後。後置譯碼器負責對正在執行的指令進行譯碼,而前置譯碼器則對將要執行的指令進行簡單譯碼。
譯碼原理
前置譯碼器與後置譯碼器都遵循同樣的譯碼原理,觀察它們的電路,都長得如下圖:
圖中,橫著的線為輸入線,豎著的線為輸出線。不難看出,譯碼器的輸出其實是一個多輸入或非邏輯。由於或非邏輯只能識別邏輯 1,所以為了能識別邏輯 0,通常還會把原資料取反後加入到輸入中。上圖中亮綠色的線與它下方的暗綠色線互為反訊號。
為了說明譯碼器原理,讓我們看一個簡化版本的譯碼器。下圖是一個 4 入 2 出的譯碼器,我們以它為模型進行說明。
前面說到,輸出是或非邏輯。所以 \(A=\overline{\overline{X_0}+X_1+X_2+\overline{X_3}}\),化簡可得 \(A=X_0\overline{X_1}\overline{X_2}X_3\),可知 A 可以識別的輸入模式為 1001
,即可以識別 0x9
。同理可得 \(B=\overline{X_1}X_2\),即 B 可以識別的輸入模式為 x10x
。
前置譯碼器輸入與輸出
前置譯碼器負責對將要執行的指令進行簡單譯碼,因此譯碼輸入是匯流排資料。匯流排資料被暫存到前置譯碼暫存器(PD)中,避免匯流排資料變化影響譯碼結果。每個時鐘週期,匯流排資料都會重新載入到 PD 中。下圖是前置譯碼器整理過後的電路圖:
應用前面介紹的分析法,可以得到前置譯碼器輸出如下:
輸出 | 匹配模式 | 說明 |
---|---|---|
A | 1xx000x0 | LDX #, LDY #, CPX #, CPY # |
B | xxx010x1 | 立即數算術指令 |
C | xxxx10x0 | 隱式(Implied)定址指令 |
D | 0xx0xx0x | 棧操作指令、流程控制指令及算術指令 |
譯碼後,A、B、C、D 又按照 \(\overline{A+B+C\overline{D}}\) 的方式組合出一個新的訊號,這個新訊號表示指令不屬於 2 週期指令。
後置譯碼器輸入與輸出
後置譯碼器負責對正在執行的指令進行譯碼,因此輸入資料是指令暫存器中的內容。此外,它的輸入還包含了當前的時鐘週期,因此可譯出當前指令是什麼型別及是第幾個週期。6502 Schematics.pdf 已經標註出了譯碼結果,本文不再贅述。GitHub 上有一個專案-emu russia-對 6502 做了非常徹底的研究,其中當然也包括對譯碼器的研究。感興趣的讀者可以參考他們的成果,裡面有譯碼器輸出的詳細說明。
後記
6502 的譯碼器佔據了晶片很大一塊地方,是指令識別的核心,粗看之下可能覺得譯碼器非常神奇或者非常複雜,希望本文的介紹能讓你祛魅。回頭再想想,其實我們可以把譯碼器看做成一個只讀記憶體,只不過儲存的是一個 130 位的超大數。正是這個原因,有時候 6502 的譯碼器又叫解碼器只讀記憶體(Decoder ROM)。
為了將輸入模式對應的指令標識出來,我做了一個小工具:
如果無法看到演示介面,可以點選此處體驗。
【說明】:
- 輸入 “xxxx10x0” 點選 “確定” 即可檢視所有匹配的指令。
- 工具還支援表示式,例如輸入 “1xx000x0 || xxx010x1 || (xxxx10x0 && ~0xx0xx0x)” 再點選 “確定” 即可檢視所有滿足表示式的指令。
參考
- 6502 Schematic.pdf
- 6502 Circuit Diagram
- EMU rassia - 6502 decoder
- 6502 decoder tool