CSSC - EP.3 指令集解析
CSSC Project前幾天打疫苗不舒服,停更了一段時間。而且期末終於結束了,雖然後面會有很多混雜期末時所做的一些決定,但還是有更多時間去思考更多細節了 (´▽`)
專案進行中,我越意識到原來設計一個計算機架構並不是這麼簡單的一件事情⋯⋯我一開始是直接用腦袋想和模擬,但越到後面發現,uxn 基本指令集就有 32 個,加上3個mode組合後共有 256 個指令集,要為 256 個指令設計一個 CPU 對於一個剛入學的大學生真的是一個有挑戰性的事情⋯⋯
首先,我沒有任何設計CPU的經驗,再此我只能用最熟悉的方式去思考與分析。
Uxn
- Uxn 是一個為全平台設計的虛擬機,目的是簡單的設計但為實現多樣的可能性
- Uxn 內建 Stack, Return Stack、Device I/O (Varvara),分別可以以不同的指令訪問。
- 可以使用 Uxntal (組語) 和 bicycle (組譯器) 來組譯 uxn 的 ROM
Uxn 會從 ROM 的 位址 0x0100 載入到 RAM 的 0x0100,通常在 ROM 內 0x0100 以前都會是空的。
Uxntal
Uxntal 和 uxn 密不可分,Uxntal 是轉為 uxn 設計的組語。對於基礎opcode uxn共有32個,並且分成 4 個區。
00~07
是基礎的 stack operation08~0f
是邏輯運算和 branch instruction10~17
是直接對memory進行操作18~1f
是 ALU 數學運算
你可以在這裡找到官方文件: https://wiki.xxiivv.com/site/uxntal.html
Stack: a b c
00 BRK
80 LIT a b c M[PC+1] 08 EQU a b?c
01 INC a b c+1 09 NEQ a b!c
02 POP a b 0a GTH a b>c
03 NIP a c 0b LTH a b<c
04 SWP a c b 0c JMP a b {PC+=c}
05 ROT b c a 0d JCN a {(b8)PC+=c}
06 DUP a b c c 0e JSR a b {rs.PC PC+=c}
07 OVR a b c b 0f STH a b {rs.c}
10 LDZ a b M[c8] 18 ADD a b+c
11 STZ a {M[c8]=b} 19 SUB a b-c
12 LDR a b M[PC+c8] 1a MUL a b*c
13 STR a {M[PC+c8]=b} 1b DIV a b/c
14 LDA a b M[c16] 1c AND a b&c
15 STA a {M[c16]=b} 1d ORA a b|c
16 DEI a b D[c8] 1e EOR a b^c
17 DEO a {D[c8]=b} 1f SFT a b>>c8l<<c8h
註:這張表預設有 a、b、c三個元素,在每個opcode後面是執行完指令後的結果。
opcode 和 mode 的關係呈現如下:
MSB < MMMOOOOO > LSB
| |
v v
mode opcode
and:
M M M = [keep, rtrn, shrt]
| | |
v | |
keep | |
| |
v |
return |
|
v
short
對於 opcode 來說:
MSB < o o O O O > LSB
| |
v v
Feature Area Opcode-number
這裡的 opcode number 指的是一個指令在一個指令區間中的編號, Feature Area 共有 0x00~0x11。
指令週期對stack的操作
但一個指令從解碼到執行並不只有辨別哪個指令要做什麼而已,而已知CPU 指令週期大致如下:
- Fetch
- Decode
- Execute
在 uxn 虛擬機中,指令週期被抽象化到每個 uxn 虛擬機的實作都可以不考慮遵守、或者沒有一個明確規範的限制。但是可以根據Uxn的特性歸納出一下結構:
- Lookup to instruction
- operate stack or store value
- execute…
- push or store back to stack
雖然在原本理論的情況下,stack必須在其中pop value,並且算完後push回去。但如果我們只考慮透過 stack pointer 實作,在有些情況下,只需要重新把相同位置的數值設為新的結果即可。
Example 1: INC2k
我們想要實作以下指令功能:
#01 INC ( 02 )
#0001 INC2 ( 00 02 )
#0001 INC2k ( 00 01 00 02 )
如果 stack 必須在指令週期 pop 和 push:
#0102 INC2k
operation step original stack register (flexable with 8-bits and 16-bits)
DUP 1 (01 02) (01)
DUP 2 (01 02) (01 02)
INC 3 (01 02) (01 03)
POP 4 (01 02 01) (03)
POP 5 (01 02 01 03) ()
其中 POP 所需的動作複雜度高。但如果不需要在指令週期 pop 和 push:
#0102 INC2k
operation step original stack counter
*LIT2 1 (01 02) (??)
DUP2 2 (01 02 01 02) (??)
DUP 3 (01 02 01 02) (02)
INC 4 (01 02 01 02) (03)
SET 5 (01 02 01 03) (03)
or, 如果有overflow的情況的話:
#01ff INC2k
operation step original stack counter description
*LIT2 1 (01 ff) (??)
DUP2 2 (01 ff 01 ff) (??)
DUP 3 (01 ff 01 ff) (ff)
INC 4 (01 ff 01 ff) (00) count as overflow, flag on
SET 5 (01 ff 01 00) (00)
DUP 6 (01 ff 01 00) (01)
INC 7 (01 ff 01 00) (02)
SET 8 (01 ff 02 00) (02)
這時你也會發現,如果有overflow的情況,只需要再調用相同的指令操作即可(即 DUP, INC, SET)。
過程省略了繁雜的POP,取而代之的是利用stack pointer實作抽象stack、並且對stack數值的直接操作。這呼應到我第一篇 EP.0 所提到的「在確認完Stack pointer有機會在74系列實作後,就開始⋯⋯」的初期評估,在專案開始之際我已經計畫使用 Stack pointer加上前面提到的模式實作抽限的stack operation。
下一篇:指令分析與指令週期
但如果要解決指令集在CU中實作的問題,就要考量到控制中樞的設計。下一個文章我將會嘗試尋找有什麼樣的方法可以協助我設計uxn指令集的實作。同時後續會繼續提到未提及的 初期 Stack pointer 的驗證和實作方式。
再者,因為篇幅上的關係,這一篇的結論草草就這樣結束了真的有點可惜。我會在下一篇的開頭提及這一篇缺乏的重點概念,也再次表達歉意。
最後,其實蠻希望CSSC系列能像筆記一樣把完整想法呈現出來,但是發現有很多情況寫出來後是很冗贅的。所以在最後我把這一篇的大部分內容都刪減掉並且重新撰寫更嚴謹的版本,希望閱讀起來能夠更加的流暢。