<h1 style="text-align: center;">系統程式 期末專題 SIC/XE 組譯器</h1> ### **1131 NTNU CSIE System Programming** * 課程名稱與代碼: 系統程式 CSU0027 (三校聯盟課程代碼: 3N1383701) * 老師: 黃冠寰 * 學生: NTUST M11212913 戴聖宴 * 本次作業用 C 語言來實現支援部分 SIC/XE 指令的組譯器: [我的 Github 連結](https://github.com/Pathfinder1996/SIC-XE-Assembler-System-Programming) * 本次作業筆記: [我的 HackMD 連結](https://hackmd.io/@Dylan-Dai/rJlpnliIye) * 如果想要每週作業和期中期末考古題,歡迎來信: daiyan.6666@gmail.com <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/By1LAlo81e.jpg" alt="63750" style="width: 200px; height:250px; margin-left: 30px;"> </div> --- ### **Figure 2.6 SIC/XE 程式** * 只需實現支援課本 Figure 2.6 上的 SIC/XE 程式碼的組譯器。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/SJMypbo8yx.png" alt="63750" style="width: 400px; height:600px; margin-left: 30px;"> </div> --- * 並產出課本 Figure 2.8 格式的 object program。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/S1pL4Zs8kl.png" alt="63750" style="width: 400px; height:200px; margin-left: 30px;"> </div> --- ### **組譯器架構設計** * 本次專題組譯器的實現是依二階段(two passes)的方式,來處理 Figure 2.6。 * 第一階段(pass1): * 掃描程式碼的 label,並計算其位址(如 Figure 2.6 的 LOC 欄),記錄到 symbol table。 * 第二階段(pass2): * 根據 mnemonic 的格式轉成對應的機器語言,例如 Figure 2.6 Line 10 的 STL 轉成 14。 * 把 symbol 的運算元轉為對應的位址,例如 Figure 2.6 Line 10 的 RETARD 轉成 1033。 * 處理常數資料,例如 Figure 2.6 Line 80 的 EOF 轉成 454F46。 * 產生 assembler table 的 object code 並輸出 object program。 * 以下為組譯器主架構: <div style="display: flex; justify-content: center;"> <table border="1" style="border-collapse: collapse; text-align: left;"> <tr> <th>模組</th> <th>功能</th> </tr> <tr> <td>OPTAB</td> <td>用來儲存有支援 mnemonic 的 opcode 及其格式,並在 Pass1 階段用。</td> </tr> <tr> <td>Pass1 階段(定義符號)</td> <td> 1. 計算程式中 directive 跟 mnemonic 的 LOC。<br> 2. 處理好 directive,像是 BYTE 或 RESW 佔用多少 LOC。<br> 3. 檢查 label 有無重複定義,還有 label 不能跟 directive 或 mnemonic 一樣。<br> </td> </tr> <tr> <td>ASMTAB</td> <td>Pass1 階段產生的 assembler table 只是用來存要給 Pass2 翻譯的程式,同時更新 LOCCTR,這階段還沒產生 object code。</td> </tr> <tr> <tr> <td>SYMTAB</td> <td>Pass1 階段產生的 symbol table,用來記錄程式每當處理過程遇到一個 label 時,LOCCTR 的現行值就是該 symbol 的位址,Pass2 階段用。</td> </tr> <tr> <td>REGTAB</td> <td>用來儲存有支援的暫存器,給 pass2 在跟 register 有關的 mnemonic 翻譯用。 </td> </tr> <tr> <td>Pass2 階段(翻譯並產生object program)</td> <td> 1. 翻譯程式(根據不同格式找到其位址)。<br> 2. 處理 BYTE 等 directive 的值到 ASMTAB 的 object code 欄。<br> 3. 輸出 object code 到 record。<br> </td> </tr> <tr> <td>OBJTAB</td> <td>儲存 Pass2 階段 ASMTAB 產生的 object code,輸出 object program 用。 </td> </tr> <tr> <td>modify</td> <td>處理程式中需要重定位的位址。 </td> </tr> <tr> <td>record</td> <td> Header record: 記錄程式的名稱、起始址、長度。<br> Text record: 這段機器碼的起始址、長度、object code。<br> Modification record: 被修改的起始位址、長度。<br> End record: 程式中第一個可執行指令的位址(也可指定 label 位址)。<br> </td> </tr> </table> </div> --- ### **Pass1 階段** * 建立 OPTAB: * 儲存 mnemonic 的 opcode 及其格式。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/S15iUiiUyg.png" alt="63750"> </div> * Pass1 階段產生的 ASMTAB: * 記錄吃進來的每一行程式的資訊。每一行分割 token 後,分別儲存到 ASMTAB 的 loc、label、opcode、operand,同時更新 LOCCTR。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/H1UVKss81l.png" alt="63750"> </div> * Pass1 階段產生的 SYMTAB: * 每當 ASMTAB 處理過程遇到一個 label 時,LOCCTR 的現行值就是該 symbol 的位址,就把 label 跟位址新增到 SYMTAB,給 Pass2 階段翻譯用(例如分支或直接定址的指令)。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/HkcOehi8ke.png" alt="63750"> </div> --- ### **Pass2 階段** * 建立 REGTAB: * 儲存課本定義的 register 及其對應的號碼,翻譯過程碰到與暫存器有關的指令時查表用。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/BydJoe281x.png" alt="63750"> </div> * 逐行翻譯並產生 object code: * directive 的處理方式: * BASE : * 設定基址(base address)。使用基底相對位址(base relative addressing)時需要設定。 * BYTE : * 如果運算元的開頭是 C 會將單引號裡面的字元轉成其對應的十六進制 ASCII 碼,複製到 OBJTAB。(例如 ASMTAB Line 17 的 EOF BYTE C'EOF' 中的 E O F 轉成 45 4F 46)。 * 而如果運算元的開頭是 X,就直接取出單引號裡面的字元,複製到 OBJTAB。(例如 ASMTAB Line 35 INPUT BYTE X'F1' 中的 F1)。 * START / RESW / RESB / END: * 上述這四種 directive 就不產生 object code。 * mnemonic 的處理方式: <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/H1JaHz3UJl.png" alt="63750" style="width: 450px; height:500px; margin-left: 30px;"> </div> * format 2 的處理方式: * 翻譯 ASMTAB 時,會去 OPTAB 看 mnemonic 是什麼格式,再看運算元是一個還是兩個 register ,去 REGTAB 查詢 register 對應的號碼,再加上 mnemonic 的 opcode 來產生 object code,並複製到 OBJTAB。(只有一個 register 的話 r2 一律帶 0) * 範例1: ASMTAB Line 21 的 CLEAR X。 ![image](https://hackmd.io/_uploads/SkCqoX3Lkg.png) * 範例2: ASMTAB Line 28 的 COMPR A,S。 ![image](https://hackmd.io/_uploads/HJ4Jn7h8kl.png) * format 3 的處理方式: * 翻譯 ASMTAB 時,會去 OPTAB 看 mnemonic 是什麼格式。 * Opcode: 只佔 6 bits ,所以轉成二進制時最右邊的兩個 bits 會被去掉。 * 初始化 flag n, i, x, b, p, e 為零。 * 設置 flag n, i: 定址模式: * n = 1, i = 1, simple 直接定址模式 * n = 0, i = 1, immediate 立即定址模式 (運算元最前面有 #) * n = 1, i = 0, indirect 間接定址模式 (運算元最前面有 @) * 設置 flag x: index 定址模式: * 如果運算元有,X 則 flag x = 1。 * 設置 flag e: 格式3固定為 0。 * 設置 flag b, p: 決定相對基底位址(base relative)模式或相對程式計數器位址(program-counter relative)模式:![image](https://hackmd.io/_uploads/r1ElASh81e.png) * 先試 program-counter relative 公式: * disp = TA - (PC)。如果 disp 在 [-2048, 2047] 之間,flag b 設置為 0, p 設置為 1,表示使用 PC relative 模式。 * 如果算出來是負值,會將 disp 加上 0x1000,並轉換為12位元的十六進制的正值。 * 而如果 disp 超出上面 PC relative 範圍,則嘗試 base relative 公式: * disp = TA - (B)。如果 disp 在 [0, 4095] 之間,flag b 設置為 1,p 設置為 0,表示使用 base relative 模式,並轉換為12位元的十六進制的正值。 * 否則: * 都不在上述範圍應該要報錯或嘗試格式4? (不在這次作業範圍,沒特別處理) * 範例1: ASMTAB Line 26 的 JEQ RLOOP。 * 先試 PC: TA = 1040, PC = 1046 --> disp = TA - (PC) = 1040 - 1046 = -6(十六進制) = -6(十進制),disp 有在 PC 範圍內,再把 -6 + 0x1000 = FFA。 * 使用 PC,flag p 設置為 1。 ![image](https://hackmd.io/_uploads/S1V2GD3Lkl.png) * 範例2: ASMTAB Line 40 的 LDCH BUFFER,X。 * 先試 PC: TA = 0036, PC = 106B --> disp = TA - (PC) = 0036 - 106B = -1035(十六進制) = -4149(十進制),disp 不在 PC 範圍內。 * 換試 base: TA = 0036, B = LENGTH = 0033 --> disp = TA - (B) = 0036 - 0033 = 3(十六進制) = 3(十進制),disp 在 base 範圍內。 * 使用 base,flag b 設置為 1。 ![image](https://hackmd.io/_uploads/Hynh4DnIJl.png) * format 4 的處理方式: * 翻譯 ASMTAB 時,若 mnemonic 前面有 + 號直接用格式4處理,用於支援 20 bits 的 target address,並將翻譯出來 object code 複製到 OBJTAB。 * Opcode: 只佔 6 bits ,所以轉成二進制時最右邊的兩個 bits 會被去掉。 * 初始化 flag n, i, x, b, p, e 為零。 * 設置 flag n, i: 定址模式: * n = 1, i = 1, simple 直接定址模式 * n = 0, i = 1, immediate 立即定址模式 (運算元最前面有 #) * n = 1, i = 0, indirect 間接定址模式 (運算元最前面有 @) * 設置 flag x: index 定址模式: * 如果運算元有,X 則 flag x = 1。 * 設置 flag b, p: 基底或相對位址模式(格式4固定為 0)。 * 設置 flag e: 格式4固定為 1。 * address: target address 佔 20 bits (沒滿的部分高位補 0)。 * 範例1: * ASMTAB Line 5 的 +JSUB RDREC。 ![image](https://hackmd.io/_uploads/B1BmgN3U1e.png) * 範例2: * ASMTAB Line 24 的 +LDT #4096。 ![image](https://hackmd.io/_uploads/rkzWfV281l.png) * format 4 需要重定位的處理方式: * 只要運算元不是 # 開頭就會記錄該 mnemonic 的位址再加上 1,進行重定位。 * 05 固定長度,表示需要修改 20 bits。 * 課本定義格式3與格式4的 flag 設置方式等(黃底為格式4): ![image](https://hackmd.io/_uploads/H1_oWQ2Ike.png) * 最終 Pass2 產生的 ASMTAB: <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/Hk8FSvnLkg.png" alt="63750"> </div> --- ### **Object Program** * 根據 OBJTAB 及其他資訊打印 record: * OBJTAB: <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/BkYPUv2Iyl.png" alt="63750"> </div> * Header record: 程式的基本資訊。 * H^<program Name>^<start Address>^<program Length> * program name: 程式名稱(佔 6 位)。 * start address: 程式的起始位址,轉換為十六進制(佔 6 位)。 * program length: 程式長度(LOCCTR - start address),轉換十六進制(佔 6 位)。 * Text record: object code 存放到多個 text record 中,並控制每條 record 的大小。 * T^<start address>^<record length>^<object code...> * start address: 當前記錄的起始位址(object code 起始址)。 * record length: 累計的 object code 大小(以位元組計算,上限為 0x1D = 29 bytes)。 * object code: 放 object code 進去,並統計當前 text record 中的指令數量。 * Modification record: 處理 object program 中需要重定位的位址(如外部符號)。 * M^<modify address>^<length> * modify address: 需要修改的位址。 * Length: 修改位元長度(5 半位,或 20 bits) * End record: 標記程式結束並指定起始執行位址。 * E^<start address> * start address:程式的入口位址,轉換為十六進制(佔 6 位)。 * 最終產出的 object program <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/ByIGoD3Uyx.png" alt="63750"> </div>