繼上一篇指令分析,這篇要做的是組譯,組譯指的是我們寫完的組合語言變成機器語言的過程。
和之前的指令分析一樣,有非常多的細節要注意。這裡的練習分寫 SIC 和 SIC/XE,有些地方不同且 SIC/XE 還有多東西要處理。
下方的內容如果有錯誤,歡迎來信更正!
SIC 組譯
下方的題目取自席家年教授的練習題,如有侵權請來信告知!
題目 PDF 下載
下方為練習卷

而我們要做的第一件事就是幫每條指令寫上位址,有些指令會發現沒有在 SIC 指令表內,而這些不在表內的指令稱為組譯指示,也就是說是寫給組譯看的,並不會實際產生程式碼,但這不代表不會佔用位址空間。
| 位址 | 標記符號 | 指令 | 運算元 |
|---|
| 2100 | EX2 | START | 2100 |
| 2100 | TST | LDA | ONE |
| 2103 | | STA | DATA |
| 2106 | | LDX | ONE |
| 2109 | | LDCH | STR,X |
| 210C | | STCH | CX |
| 210F | | RSUB | |
| 2112 | DATA | RESW | 1 |
| 2115 | ONE | WORD | 1 |
| 2118 | STR | BYTE | C'01' |
| 211A | CX | RESB | 1 |
| | END | TST |
上方這張表多了位址這個欄位,而欄位的計算重點如下:
- 位址開始從 START 指令的運算元開始
- START 後的第一條指令的位址和 START 相同,因為 START 不會佔用位址空間
- 位址是使用 byte 做為計數,而 SIC 表內的每條指令都佔用 24 個 bit,也就是 3 個 byte,因此可以發現到表內指令的位址皆為 +3
- 所有的運算元皆使用十進位,除了 START 之外
- RESW 代表此處保留指定數量的 word,一個 word 代表三個 byte
- WORD 代表此處要有一個長度為 word 的整數,運算元即為整數的值
- RESB 代表此處保留指定數量的 byte
- BYTE 的運算元 C'01' 的 01 代表的是字元,因此此處可以知道有兩個字元,也就是兩 byte
算出所有的位址之後就下以完成「標記符號位址表(SYMTAB)」這個表格
| 標記符號 | 位址 |
|---|
| EX2 | 2100 |
| TST | 2100 |
| DATA | 2112 |
| ONE | 2115 |
| STR | 2118 |
| CX | 211A |
接著我們要開始寫目的程式,但在寫之前要先把在 SIC 表內的指令轉成目的碼,其實就是把我前一篇指令分析的步驟倒過來。
| 標註 | 指令 | 運算元 | 位址 | 目的碼 |
|---|
| EX2 | START | 2100 | 2100 | |
| TST | LDA | ONE | 2100 | 002115 |
| STA | DATA | 2103 | 0C2112 |
| LDX | ONE | 2106 | 042115 |
| LDCH | STR,X | 2109 | 50A118 |
| STCH | CX | 210C | 54211A |
| RSUB | | 210F | 4C0000 |
| DATA | RESW | 1 | 2112 | |
| ONE | WORD | 1 | 2115 | 000001 |
| STR | BYTE | C'01' | 2118 | 3031 |
| CX | RESB | 1 | 211A | |
| END | TST | 211B | |
上方這張表多了目的碼這個欄位,而欄位的計算重點如下:
- 指令可以透過 SIC 指令表取得對應的 opcode,而 opcode 就是前八位二進位
- 如果運算元有
,X 表示要使用索引定址,則我們把二進位的第九位設為 1
- 其於的 15 位則可以參考標記符號位址表填入
- BYTE 直接把字元換成 ASCII 再換成十六進制寫入
- WORD 直接寫入運算元換成十六進制的值即可,但要記得保留前面的
0,總共要有六位。
下面以 LDCH STR,X 這條指令為例:
我們取得 LDCH 的 opcode 為 50,二進位為 0101 0000,這代表前八位。因為有 ,x,因此要使用索引定址,我們把第九位設為 1,反之為 0。
最後把剩下的 STR 的位址 2118 轉為二進位,得到 010 0001 0001 1000,可以發現到第一組只有三位,因為只有 15 位。
接著把所有的二進位組合起來 0101 0000 1 010 0001 0001 1000,再換回十六進位就可以得到 50A118。
目的程式
接下來真的可以開始寫目的程式了,目的程式要一行一行的寫,且所有的地方都以十六進位表示。這邊特別提一下,正常的程式不會有 ,,這裡是為了清楚顯示才加的。
第一行是表頭紀錄,要依序寫入的資訊如下:
- 開頭字母
H
- 六格的程式名稱,如果不足則直接補空格
- 六格的開始位址,如果不足則直接補零
- 六格的程式長度,如果不足則直接補零
以這份程式為例,表頭紀錄如下:
H,EX2☐☐☐,002100,00001B
值得注意的是程式長度的算法就是 END 位址減掉 START 的位址
接下來會有數行的本文紀錄,要依序寫入的資訊如下:
- 開頭字母
T
- 六格的開始位址,如果不足則直接補零
- 兩格的程式長度,如果不足則直接補零。這個可以最後再寫
- 從上到下的目的地,連續往後寫不加空格,直到遇到指令沒有目的碼,則直接重新開始一行
以這份的程式為例,可以寫出兩行如下:
T,002100,12,002115,0C2112,042115,50A118,54211A,4C0000
T,002115,05,000001,3031
最後一行是結束紀錄,但是這裡因為剛好遇到有指令沒有目的碼,因此可以直接不寫 END。
這裡就完成了 SIC 的指令組譯了。
SIC/XE 組譯
大部分的步驟都和 SIC 組譯相同,我會特別針對不同的地方點出來,相同的地方我不會再提。
題目 PDF 下載
下方為練習卷

和 SIC 大部分相同,先把位址寫一寫,寫完的結果如下:
| 位址 | 標記符號 | 指令 | 運算元 |
|---|
| 0 | | START | 0 |
| 0 | BB | LDB | #BB |
| - | | BASE | BB |
| 3 | | LDA | KK |
| 6 | | +STA | NN |
| A | KK | WORD | 15 |
| D | BUF | RESB | 4096 |
| 100D | | LDA | @NN |
| 1010 | | STA | BUF,X |
| 1013 | NN | RESW | 1 |
| 1016 | | END | BB |
特別注意到 +STA,這個代表的是它佔了四個 byte,因此計算的時候要 +4。
接著我們就直接寫目的碼,寫完如下:
| 位址 | 標記符號 | 指令 | 運算元 | 目的碼 |
|---|
| 0 | TEST | START | 0 | |
| 0 | BB | LDB | #BB | 692FFD |
| - | | BASE | BB | |
| 3 | | LDA | KK | 032004 |
| 6 | | +STA | NN | 0F101013 |
| A | KK | WORD | 15 | 00000F |
| D | BUF | RESB | 4096 | |
| 100D | | LDA | @NN | 022003 |
| 1010 | | STA | BUF,X | 0FC00D |
| 1013 | NN | RESW | 1 | |
| 1016 | | END | BB | |
在開始之前請確保已經學會了指令分析,因為很多東西我不會再提。這裡我會選幾條需要講解的來做,其它的做完可以參考上方。
LDB #BB
同樣的先找到 LDB 的 opcode 68,之後就可以發現到運算元前面有個 # 的符號,這和 ni 有關,可以參考下方的整理:
因此這裡可以知道 ni 要是 01。
而我們還有 xbp 要決定,決定的方法如下:
- x 只有運算元指定的時候才使用
- b 參考 p
- p 只要是運算元是標記符號就使用,但需要注意如果 TA 會超出範圍 −2048≤PC≤2047,則換成 b。
- 如果是格式四則全為
0
最後還有一個 e,這個的決定就是運算子前面有沒有 + 號,如果有的話就會變成格式四。
但到這裡我們只決定了 nixbpe,我們還有最後的 12 位要算。還記得之前做指令分析的時候會把最後 12 位加上 xbp 之類的暫存器的位址嗎?這裡我們要反算,我們要讓算出來的值剛好落在我們想要的地方。
這裡我們的目標是 BB,也就是 0 這個位址,而 PC 此時是多少呢?PC 指的是下一行的位址,在這裡是 3,因此我們就知道這 12 byte 要是 -3,這樣一來才能跟 PC 做抵銷,來到達我們想要的地。注意一下 -3 要使用二的補數來表達。
而這樣我們就全部都有了,我們就能寫出 0110 1001 0010 1111 1111 1101,換成十六進位就是 692FFD。
+STA NN
這條可以發現到前面有 + 號,因此要使用格式四的方式來寫,也就是說會有 32 位,寫出來的目的碼也會明顯比較長。
除此之外,xbp 都應該要是 0。
LDA @NN
這條沒有什麼特別的,但可以發現到運算元前面有 @,也就是說 ni 要是 10。
STA BUF,X
這條可以發現到有一個 ,X,可以知道 xbp 的 x 要設為 1。除此之外還會發現到後 12 位算完會超出範圍,這個時候就要把 p 改為 0 且把 b 改為 1。
這個時候的後 12 位就不是在使用 PC 去做計算,而是要使用 BASE 所在的位址,也就是 0。
因為剛好是零,因此後 12 位就剛好是 BUF 的位址。
目的程式
當寫完全部的目的碼之後就可以開始寫目的程式,第一行的表頭紀錄和 SIC 一樣,如下:
H,TEST☐☐,000000,001016
接下來一樣會有數行的本文紀錄,跟之前完全一樣,如下:
T,000000,0D,692FFD,032004,0F101013,00000F
T,00100D,06,022003,0FC00D
再接下來會有一個跟 SIC 不同的地方,稱為修正紀錄。為什麼會需要這個紀錄是因為有時候我們使用的定址是直接寫死的,也就是說不是用 PC 之類的方法算出來的,而是直接寫死某個地方,但程式會被放在記憶體的哪裡我們不知道,因此寫死的位址就會在運作的時候出錯。
以這題為例,可以發現到 +STA NN 的 xbp 都是 0,也就是說它的位址寫死了,因此它就需要修正。
每一個需要修正的指令都要一行修正紀錄,內容依序寫入:
- 開頭字母
M
- 六格要修正的指令後 20 位或後 12 位所在的位址
- 兩格要修正的位數,位數要再除於四
- 符號
+
- 六格 START 位址的標記符號
以這份的程式為例,修正紀錄如下:
M,000007,05,+,TSET☐☐
這裡的長度其實只會有兩種可能,如果是格式三就一定是 3,如果是格式四就一定是 5。
結束紀錄如下,就填上 BB 代表的位址即可:
E,000000