Skip to main content

大戰系統程式 - SIC 及 SIC/XE 組譯

· 14 min read

繼上一篇指令分析,這篇要做的是組譯,組譯指的是我們寫完的組合語言變成機器語言的過程。

和之前的指令分析一樣,有非常多的細節要注意。這裡的練習分寫 SIC 和 SIC/XE,有些地方不同且 SIC/XE 還有多東西要處理。

下方的內容如果有錯誤,歡迎來信更正!

SIC 組譯

下方的題目取自席家年教授的練習題,如有侵權請來信告知!

題目 PDF 下載

下方為練習卷 練習卷

而我們要做的第一件事就是幫每條指令寫上位址,有些指令會發現沒有在 SIC 指令表內,而這些不在表內的指令稱為組譯指示,也就是說是寫給組譯看的,並不會實際產生程式碼,但這不代表不會佔用位址空間。

位址標記符號指令運算元
2100EX2START2100
2100TSTLDAONE
2103STADATA
2106LDXONE
2109LDCHSTR,X
210CSTCHCX
210FRSUB
2112DATARESW1
2115ONEWORD1
2118STRBYTEC'01'
211ACXRESB1
ENDTST

上方這張表多了位址這個欄位,而欄位的計算重點如下:

  • 位址開始從 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)」這個表格

標記符號位址
EX22100
TST2100
DATA2112
ONE2115
STR2118
CX211A

接著我們要開始寫目的程式,但在寫之前要先把在 SIC 表內的指令轉成目的碼,其實就是把我前一篇指令分析的步驟倒過來。

標註指令運算元位址目的碼
EX2START21002100
TSTLDAONE2100002115
STADATA21030C2112
LDXONE2106042115
LDCHSTR,X210950A118
STCHCX210C54211A
RSUB210F4C0000
DATARESW12112
ONEWORD12115000001
STRBYTEC'01'21183031
CXRESB1211A
ENDTST211B

上方這張表多了目的碼這個欄位,而欄位的計算重點如下:

  • 指令可以透過 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/XE 組譯練習卷

和 SIC 大部分相同,先把位址寫一寫,寫完的結果如下:

位址標記符號指令運算元
0START0
0BBLDB#BB
-BASEBB
3LDAKK
6+STANN
AKKWORD15
DBUFRESB4096
100DLDA@NN
1010STABUF,X
1013NNRESW1
1016ENDBB

特別注意到 +STA,這個代表的是它佔了四個 byte,因此計算的時候要 +4。

接著我們就直接寫目的碼,寫完如下:

位址標記符號指令運算元目的碼
0TESTSTART0
0BBLDB#BB692FFD
-BASEBB
3LDAKK032004
6+STANN0F101013
AKKWORD1500000F
DBUFRESB4096
100DLDA@NN022003
1010STABUF,X0FC00D
1013NNRESW1
1016ENDBB

在開始之前請確保已經學會了指令分析,因為很多東西我不會再提。這裡我會選幾條需要講解的來做,其它的做完可以參考上方。

LDB #BB

同樣的先找到 LDB 的 opcode 68,之後就可以發現到運算元前面有個 # 的符號,這和 ni 有關,可以參考下方的整理:

  • 無符號 11
  • # 01
  • @ 10

因此這裡可以知道 ni 要是 01

而我們還有 xbp 要決定,決定的方法如下:

  • x 只有運算元指定的時候才使用
  • b 參考 p
  • p 只要是運算元是標記符號就使用,但需要注意如果 TA 會超出範圍 2048PC2047-2048 \leq PC \leq 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