x213212
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# 針對組譯器一次分析 https://github.com/programmermagazine/201306/blob/master/source/article5.md 作者是陳鐘誠老師,剛好也挖到了些源碼 來小分析一下 程式好像也沒漏掉什麼,編譯環境還是 DEVC++ 剛好也跑得起來想說直接來針對這來分析一下,vscode 掛 GDB 好有點麻煩還有要配置檔以後有空弄 比較難得的是它裡面有寫註解,看起來是個好上手的東西 # 什麼是組譯器編譯器 什麼是組譯器應該是在 把 中間碼轉為 組合語言轉成 目的碼這塊 是交由 我們的 組譯器來跑 然後編譯器是把 高階語言 C /C ++ 翻成中間碼 在 翻成 機械碼 ![](https://i.imgur.com/WROwvOQ.png) 所以類似這個概念 前端 中間碼 後端 那這個下面這個程式就是負責翻譯我們的組合語言 成 目的碼 # 先翻 Makefile 可以看到我們的 assmebler 在 as0 那一行 ```makefile CC = gcc.exe -D__DEBUG__ OBJ = Parser.o Tree.o Lib.o Scanner.o Array.o Compiler.o HashTable.o Generator.o Assembler.o Cpu0.o OpTable.o LINKOBJ = $(OBJ) LIBS = INCS = BIN = test.exe c0c.exe as0.exe vm0.exe CFLAGS = $(INCS) -g3 RM = rm -f .PHONY: all clean all: $(OBJ) test c0c as0 vm0 test: $(OBJ) $(CC) main.c $(LINKOBJ) -DTARGET=TEST -o test $(LIBS) c0c: $(OBJ) $(CC) main.c $(LINKOBJ) -DTARGET=C0C -o c0c $(LIBS) as0: $(OBJ) $(CC) main.c $(LINKOBJ) -DTARGET=AS0 -o as0 $(LIBS) vm0: $(OBJ) $(CC) main.c $(LINKOBJ) -DTARGET=VM0 -o vm0 $(LIBS) clean: ${RM} $(OBJ) $(BIN) Parser.o: Parser.c $(CC) -c Parser.c -o Parser.o $(CFLAGS) Tree.o: Tree.c $(CC) -c Tree.c -o Tree.o $(CFLAGS) Lib.o: Lib.c $(CC) -c Lib.c -o Lib.o $(CFLAGS) Scanner.o: Scanner.c $(CC) -c Scanner.c -o Scanner.o $(CFLAGS) Array.o: Array.c $(CC) -c Array.c -o Array.o $(CFLAGS) Compiler.o: Compiler.c $(CC) -c Compiler.c -o Compiler.o $(CFLAGS) HashTable.o: HashTable.c $(CC) -c HashTable.c -o HashTable.o $(CFLAGS) Generator.o: Generator.c $(CC) -c Generator.c -o Generator.o $(CFLAGS) Assembler.o: Assembler.c $(CC) -c Assembler.c -o Assembler.o $(CFLAGS) Cpu0.o: Cpu0.c $(CC) -c Cpu0.c -o Cpu0.o $(CFLAGS) OpTable.o: OpTable.c $(CC) -c OpTable.c -o OpTable.o $(CFLAGS) ``` # 我們再來看 main.c 發現他是根據 makefile 裡面的 tag去產生 各個執行檔 我們看到了我們的 AS0這一塊 他接受兩個參數 這邊我有先大致看過了,大概是 輸入ASM 產生 OBJ ```c #include "Assembler.h" // 引用組譯器檔頭 #include "Compiler.h" // 引用編譯器檔頭 #define TEST 1 // 編譯目標 1: test #define C0C 2 // 編譯目標 2: c0c #define AS0 3 // 編譯目標 3: as0 #define VM0 4 // 編譯目標 4: vm0 void argError(char *msg) { // 處理參數錯誤的情況 printf("%s\n", msg); exit(1); } int main(int argc, char *argv[]) { // 主程式開始 char cFile0[]="test.c0", *cFile=cFile0; // 預設程式檔為 test.c0 char asmFile0[]="test.asm0", *asmFile=asmFile0; // 預設組合語言為test.asm0 char objFile0[]="test.obj0", *objFile=objFile0; // 預設目的檔為 test.obj0 #if TARGET==TEST // 如果編譯目標為 TEST ArrayTest(); // 測試陣列物件 HashTableTest(); // 測試雜湊表物件 OpTableTest(); // 測試指令表物件 compile(cFile, asmFile); // 測試編譯器 assemble(asmFile, objFile); // 測試組譯器 runObjFile(objFile); // 測試虛擬機器 checkMemory(); // 檢查記憶體使用狀況 #elif TARGET==C0C // 如果編譯目標為 C0C if (argc == 3) { // 如果有 3 個參數 cFile=argv[1]; asmFile=argv[2]; // 設定參數 } else // 否則 argError("c0c <c0File> <asmFile>"); // 提示程式執行方法 compile(cFile, asmFile); // 開始編譯 #elif TARGET==AS0 // 如果編譯目標為 AS0 if (argc == 3) { // 如果有 3 個參數 asmFile=argv[1]; objFile=argv[2]; // 設定參數 } else // 否則 argError("as0 <asmFile> <objFile>"); // 提示程式執行方法 assemble(asmFile, objFile); // 開始組譯 #elif TARGET==VM0 // 如果編譯目標為 VM0 if (argc == 2) // 如果有 2 個參數 objFile=argv[1]; // 設定參數 else // 否則 argError("vm0 <objFile>"); // 提示程式執行方法 runObjFile(objFile); // 開始執行 (虛擬機) #endif system("pause"); // 暫停 (給 Dev C++ 使用的) return 0; } ``` # input ```asm LD R1, B ST R1, A JMP B RET A: RESW 1 B: WORD 29 ``` # Assemble.c 那我們可以直接看到 我們的 主要進入店 他產生一組 **Assembler *a = AsmNew(); 然後開檔讀入 text char 通過 ** AsmPass1(a, text);** **HashTableEach(a->symTable, (FuncPtr1) AsmCodePrintln);** **AsmPass2(a); ** **AsmSaveObjFile(a, objFile);** 其中我們要注意的是我們的 Pass1 和 pass2 分別是對我們的 pass1轉成絕對定址, 和 對我們的 text 做初步 的分析 pass2轉成 相對定址 ,最後由 Pass2 產生 目的碼 HashTableEach 則是把我們的最終 asm 檔的 特殊符號 比如說變數的宣告等等 都丟到我們的 hash 表去做 符號表的 name 與 address。 ```C #include "Assembler.h" void assemble(char *asmFile, char *objFile) { // 組譯器的主要函數 printf("Assembler:asmFile=%s objFile=%s\n", asmFile,objFile); // 輸入組合語言、輸出目的檔 printf("===============Assemble=============\n"); char *text = newFileStr(asmFile); // 讀取檔案到 text 字串中 Assembler *a = AsmNew(); AsmPass1(a, text); // 第一階段:計算位址 printf("===============SYMBOL TABLE=========\n"); HashTableEach(a->symTable, (FuncPtr1) AsmCodePrintln); // 印出符號表 AsmPass2(a); // 第二階段:建構目的碼 AsmSaveObjFile(a, objFile); AsmFree(a); // 輸出目的檔 freeMemory(text); // 釋放記憶體 } ``` 這邊我們看到 AsmNew 建構元做了什麼事 codes 是指令集 list symTable 這邊是塞符號表 opTable 是塞 opcode table ```c Assembler* AsmNew() { Assembler *a=ObjNew(Assembler, 1); a->codes = ArrayNew(1); a->symTable = HashTableNew(127); a->opTable = OpTableNew(); return a; } ``` # lib.h 可以看到 ObjNew 這邊 我們追到了 lib.h ```c #define ObjNew(type, count)newMemory(count*sizeof(type)) ``` # lib.c ```c // 記憶體配置函數 int newMemoryCount = 0; void* newMemory(int size) { void *ptr=malloc(size); assert(ptr != NULL); memset(ptr, 0, size); // printf("memGet:%p\n", ptr); newMemoryCount++; return ptr; } ``` 所以應該是申請一個空間,來制定我們的Assembler a大小? # ArrayNew 這邊又知道我們的 codes 是用來儲存我們拆成指令 陣列 ![](https://i.imgur.com/bM7gLVo.png) ```c a->codes = ArrayNew(1); ``` # Array.c ```c void ArrayAdd(Array *array, void *item) { ASSERT(array->count <= array->size); if (array->count == array->size) { int newSize = array->size*2; void **newItems = ObjNew(void*, newSize); memcpy(newItems, array->item, array->size*sizeof(void*)); printf("array grow from %d to %d\n", array->count, newSize); ObjFree(array->item); array->item = newItems; array->size = newSize; } array->item[array->count++] = item; printf("add item = %s\n", item); } ``` # symTable 這邊是塞符號表 也就是在 ASM 裡面的 :a :b這些 符號 它們會根據我們一開始制定的規則 已經查詢過的運算碼 新增到 hash表 變為唯一值 ![](https://i.imgur.com/qg4YtGJ.png) # opTable CPU0 的指令分為三種類型,L 型通常為載入儲存指令、A 型以算術指令為主、J 型則通常為跳躍指令,下圖顯示了這三種類型指令的編碼格式。 ![](https://i.imgur.com/c4O8xC5.png) 下面是 cpu0 指令表 ![](https://i.imgur.com/rfIVFlH.png) # AsmPass1 ```c void AsmPass1(Assembler *a, char *text) { // 第一階段的組譯 int i, address = 0, number; Array* lines = split(text, "\r\n", REMOVE_SPLITER); // 將組合語言分割成一行一行 ArrayEach(lines, strPrintln); // 印出以便觀察 printf("=================PASS1================\n"); for (i=0; i<lines->count; i++) { // 對於每一行 strReplace(lines->item[i], SPACE, ' '); AsmCode *code = AsmCodeNew(lines->item[i]); // 建立指令物件 code->address = address; // 設定該行的位址 Op *op = HashTableGet(opTable, code->op); // 查詢運算碼 if (op != NULL) { // 如果查到 code->opCode = op->code; // 設定運算碼 code->type = op->type; // 設定型態 } if (strlen(code->label)>0) // 如果有標記符號 HashTablePut(a->symTable, code->label, code); // 加入符號表中 ArrayAdd(a->codes, code); // 建構指令物件陣列 list AsmCodePrintln(code); // 印出觀察 code->size = AsmCodeSize(code); // 計算指令大小 address += code->size; // 計算下一個指令位址 } ArrayFree(lines, strFree); // 釋放記憶體 } ``` pass1 做了 絕對定址的動作和 為我們的 產生 目的碼 也就是 ![](https://i.imgur.com/wjcrAHs.png) 這個地方 可以看到我們的 經過 pass1 後 我們的 組合語言 被展開成 ```code LD R1, B ``` address asm 指令型態 和 所使用 暫存器 r1 和最後的 相對定址 .. ```code 0000 LD R1, B L 0 (NULL) ``` # for 迴圈 我們直接看迴圈裏面可以看到 我們的 strReplace 去做去除 換行之類的動作 ![](https://i.imgur.com/lHeNrqU.png) 接下來再進行我們的 AsmCode 我們這邊把我們剛剛處理完的 lines[i]取出來,也就是第一行 我們可以看到 函數 **AsmCodeNew** 幫我們把我們取出來的 該行數 進行初步字串處理 ```c AsmCode* AsmCodeNew(char *line) { AsmCode* code = ObjNew(AsmCode,1); char label[100]="", op[100]="", args[100]="", temp[100]; int count = sscanf(line, "%s %s %[^;]", label, op, args); if (strTail(label, ":")) { strTrim(temp, label, ":"); strcpy(label, temp); } else { strcpy(label, ""); sscanf(line, "%s %[^;]", op, args); } // printf("label=%s op=%s args=%s\n", code->label, op, args); code->label = newStr(label); code->op = newStr(op); strTrim(temp, args, SPACE); code->args = newStr(temp); code->type = ' '; code->opCode = OP_NULL; // AsmCodePrintln(code); return code; } ``` 處理完後我們的 code 在呼叫完 **AsmCodeNew** 返回 **AsmCode** 這個結構。 **AsmCode** ```c typedef struct { // 指令物件 int address, opCode, size; // 包含位址、運算碼、 char *label, *op, *args, type; // 空間大小、op, 、標記、 char *objCode; // 參數、型態、目的碼 } AsmCode; // 等欄位 ``` # hit hash table 我們看到這邊 可以看到我們去用我們分析完的 code-> op code 去 hit 我們的 opTable 代表我們去查表看我們的 cpu 支不支援我們的 opcode ```c code->address = address; // 設定該行的位址 Op *op = HashTableGet(opTable, code->op); // 查詢運算碼 if (op != NULL) { // 如果查到 code->opCode = op->code; // 設定運算碼 code->type = op->type; // 設定型態 } ``` # insert symTable 假設我們的符號也就是 : 開頭的被我們的 **AsmCodeNew** 分析道 裡面的 code->label 不等於 0 , 我們就把到目前我們對 code 的動作 全部 insert 到我們的symTable , ```c if (strlen(code->label)>0) // 如果有標記符號 HashTablePut(a->symTable, code->label, code); // 加入符號表中 ``` # count code size and initialize address 這邊我們的 ArrayAdd 把我們的 剛剛對組合語言額外做的判斷 會導致我的的 指令 size 空間會被重新計算 所以我們要產生新的 Asmcode 加到我們一開始的 指令集 list ```c ArrayAdd(a->codes, code); // 建構指令物件陣列 list AsmCodePrintln(code); // 印出觀察 code->size = AsmCodeSize(code); // 計算指令大小 address += code->size; // 計算下一個指令位址 ``` # AsmCodeSize 這邊可以看到我們在完成上述絕對定址後還需要對 我們的變數類進行分配記憶體空間,我們會在下面進行小分析。 ```C int AsmCodeSize(AsmCode *code) { // 計算指令的大小 switch (code->opCode) { // 根據運算碼 op case OP_RESW: // 如果是RESW return 4 * atoi(code->args); // 大小為 4*保留量 case OP_RESB: // 如果是RESB return atoi(code->args); // 大小為 1*保留量 case OP_WORD: // 如果是WORD return 4 * (strCountChar(code->args, ",") + 1); // 大小為 4*參數個數 case OP_BYTE: // 如果是BYTE return strCountChar(code->args, ",") + 1; // 大小為1*參數個數 case OP_NULL: // 如果只是標記 return 0; // 大小為 0 default: // 其他情形 (指令) return 4; // 大小為 4 } } ``` # AsmPass2 這邊就要對我們的指令進行編碼動作 根據我們的 程式計數器 pc 來進行相對定址。 ```c void AsmPass2(Assembler *a) { // 組譯器的第二階段 printf("=============PASS2s==============\n"); int i; for (i=0; i<a->codes->count; i++) { // 對每一個指令 AsmCode *code = a->codes->item[i]; AsmTranslateCode(a, code); // 進行編碼動作 // printf("ssssss\n"); // 輸入組合語言、輸出目的檔 AsmCodePrintln(code); } } ``` # AsmTranslateCode 這邊我們先針對 我們各個case 進行分析 ```c void AsmTranslateCode(Assembler *a, AsmCode *code) { // 指令的編碼函數 char p1[100], p2[100], p3[100], pt[100]; int ra=0, rb=0, rc=0, cx=0; char cxCode[9]="00000000", objCode[100]="", args[100]=""; strcpy(args, code->args); strReplace(args, ",", ' '); printf("address now :%d\n" , code->address) ; int pc = code->address + 4; // 提取後PC為位址+4 switch (code->type) { // 根據指令型態 case 'J' : // 處理 J 型指令 if (!strEqual(args, "")) { AsmCode *labelCode = HashTableGet(a->symTable,args); // 取得符號位址 cx = labelCode->address - pc; // 計算 cx 欄位 sprintf(cxCode, "%8x", cx); printf("address next:%d\n" , pc); printf("labelCode address next:%d\n" , labelCode->address); } sprintf(objCode, "%2x%s", code->opCode, &cxCode[2]); // 編出目的碼(16進位) //printf("%2x%s\n", code->opCode, &cxCode[2]); break; case 'L' : sscanf(args, "R%d %s", &ra, p2); if (strHead(p2, "[")) { sscanf(p2, "[R%d+%s]", &rb, pt); if (sscanf(pt, "R%d", &rc)<=0) sscanf(pt, "%d", &cx); } else if (sscanf(p2, "%d", &cx)>0) { } else { AsmCode *labelCode = HashTableGet(a->symTable, p2); cx = labelCode->address - pc; rb = 15; // R[15] is PC } sprintf(cxCode, "%8x", cx); sprintf(objCode, "%2x%x%x%s", code->opCode, ra, rb, &cxCode[4]); // printf("%s\n",cxCode); // 輸入組合語言、輸出目的檔 // printf("%shahha\n", objCode); // 輸入組合語言、輸出目的檔 break; case 'A' : // 處理 A 型指令 sscanf(args, "%s %s %s", p1, p2, p3); // 取得參數 sscanf(p1, "R%d", &ra); // 取得ra暫存器代號 sscanf(p2, "R%d", &rb); // 取得rb暫存器代號 if (sscanf(p3, "R%d", &rc)<=0) // 取得rc暫存器代號 sscanf(p3, "%d", &cx); // 或者是 cx 參數 sprintf(cxCode, "%8x", cx); sprintf(objCode, "%2x%x%x%x%s", code->opCode,ra,rb,rc,&cxCode[5]); // 編出目的碼(16進位) break; case 'D' : { // 處理是資料宣告 // 我們將資料宣告 RESW, RESB, WORD, BYTE 也視為一種指令,其形態為 D char format4[]="%8x", format1[]="%2x", *format = format1; switch (code->opCode) { // 如果是 RESW case OP_RESW: // 或 RESB case OP_RESB: // memset(objCode, '0', code->size*2); // 目的碼為 0000…. objCode[code->size*2] = '\0'; break; // 如果是 WORD: case OP_WORD: format = format4; // 設定輸出格式為 %8x case OP_BYTE: { // 如果是 BYTE : 輸出格式為 %2x Array *array = split(args, " ", REMOVE_SPLITER); // 其目的碼為每個數字轉為16進位的結果 char *objPtr = objCode; int i=0; for (i=0; i<array->count; i++) { char *item = array->item[i]; if (isdigit(item[0])) sprintf(objPtr, format, atoi(item)); else { AsmCode *itemCode = HashTableGet(a->symTable, item); sprintf(objPtr, format, itemCode->address); } objPtr += strlen(objPtr); } ArrayFree(array, strFree); break; } // case OP_BYTE: } // switch break; } // case 'D' default: strcpy(objCode, ""); break; } strReplace(objCode, " ", '0'); strToUpper(objCode); code->objCode = newStr(objCode); } ``` # J case 在處理 J型指令可以查看至 ![](https://i.imgur.com/wBqRh4a.png) 可以得知是由 一個 op 配一個 常數 c 那麼當我們的 程式 進行指令擷取的時候我們的程式計數器因為是cpu0的架構 每一個指令均佔 4 byte 所以每次進行指令擷取 都會讓我們的程式計數器 位置 往上加 4 byte 。 以我們的 Jcase 來說 我們一開始會拿我們的 args 也就是 op code 後面的 R1 B > args :R1 B > address now :0 > 0000 LD R1, B L 0 00100000 > 由於我們的 J指令專門存放 JMP 所以可能只有實作符號類型跳轉? 暫存器 或 特殊符號 ,所以照這樣跑的話,我們最上面的 INPUT 的 ASM JMP 那一欄位就是只有 一個特殊符號 B 但是 B 的位置是 0014 所以 我們在 PASS1 的時候就已經計算過 在初始化 變數 b Address 現在就是在計算 我們目前程式執行到的程式計數器 pc 到 b Address 的 offset。 ![](https://i.imgur.com/aGtAWT7.png) ```c printf("all args :%s\n", args); printf("address now :%d\n" , code->address) ; int pc = code->address + 4; // 提取後PC為位址+4 switch (code->type) { // 根據指令型態 case 'J' : // 處理 J 型指令 if (!strEqual(args, "")) { AsmCode *labelCode = HashTableGet(a->symTable,args); // 取得符號位址 cx = labelCode->address - pc; // 計算 cx 欄位 sprintf(cxCode, "%8x", cx); printf("labelCode args:%s\n", args); printf("address next:%d\n" , pc); printf("labelCode address next:%d\n" , labelCode->address); } ``` # L case 在處理 L指令可以查看至 ![](https://i.imgur.com/wBqRh4a.png) > all args :R1 B address now :0 0000 LD R1, B L 0 00100000 all args :R1 A address now :4 0004 ST R1, A L 1 01100000 > 在這邊可以看到我們的 指令已經被拆成R1 B 裡面幾個 狀況可以分成 1. ra rb cx 都有值 1. 只有 cx 1. cx 是 特殊標記符號 ```C case 'L': sscanf(args, "R%d %s", &ra, p2); if (strHead(p2, "[")) { sscanf(p2, "[R%d+%s]", &rb, pt); if (sscanf(pt, "R%d", &rc) <= 0) sscanf(pt, "%d", &cx); } else if (sscanf(p2, "%d", &cx) > 0) { } else { AsmCode *labelCode = HashTableGet(a->symTable, p2); cx = labelCode->address - pc; rb = 15; // R[15] is PC } sprintf(cxCode, "%8x", cx); sprintf(objCode, "%2x%x%x%s", code->opCode, ra, rb, &cxCode[4]); break; ``` # A case 在處理 A指令可以查看至 ![](https://i.imgur.com/wBqRh4a.png) 這邊可以看到 我們可以直接把我們的 args 直接分配到我們的 ra rb rc cs暫存器 ,稍微小懷疑一下 ![](https://i.imgur.com/ZcQayUB.png) 在 CPU0的架構 A指令 集 好像只有對 暫存器做比較所以沒有沒有變數之類的東西 也就是訪問符號表(?。 ```c case 'A': // 處理 A 型指令 sscanf(args, "%s %s %s", p1, p2, p3); // 取得參數 sscanf(p1, "R%d", &ra); // 取得ra暫存器代號 sscanf(p2, "R%d", &rb); // 取得rb暫存器代號 if (sscanf(p3, "R%d", &rc) <= 0) // 取得rc暫存器代號 sscanf(p3, "%d", &cx); // 或者是 cx 參數 sprintf(cxCode, "%8x", cx); sprintf(objCode, "%2x%x%x%x%s", code->opCode, ra, rb, rc, &cxCode[5]); // 編出目的碼(16進位) break; ``` # D case 在處理 D指令 // 我們將資料宣告 RESW, RESB, WORD, BYTE 也視為一種指令,其形態為 D 意味著我們是直接 ```c case 'D': { // 處理是資料宣告 // 我們將資料宣告 RESW, RESB, WORD, BYTE 也視為一種指令,其形態為 D char format4[] = "%8x", format1[] = "%2x", *format = format1; switch (code->opCode) { // 如果是 RESW case OP_RESW: // 或 RESB case OP_RESB: // memset(objCode, '0', code->size * 2); // 目的碼為 0000…. objCode[code->size * 2] = '\0'; break; // 如果是 WORD: case OP_WORD: format = format4; // 設定輸出格式為 %8x case OP_BYTE: { // 如果是 BYTE : 輸出格式為 %2x Array *array = split(args, " ", REMOVE_SPLITER); // 其目的碼為每個數字轉為16進位的結果 char *objPtr = objCode; int i = 0; for (i = 0; i < array->count; i++) { char *item = array->item[i]; if (isdigit(item[0])) sprintf(objPtr, format, atoi(item)); else { AsmCode *itemCode = HashTableGet(a->symTable, item); sprintf(objPtr, format, itemCode->address); } objPtr += strlen(objPtr); } ArrayFree(array, strFree); break; } // case OP_BYTE: } // switch break; } // case 'D' ``` ## OP_RESW OP_RESB ![](https://i.imgur.com/rDyrc3w.png) * RESB 保留所示數量的位元組,供資料區使用 * RESW 保留所示數量的字組,供資料區使用 ```c case OP_RESW: // 或 RESB case OP_RESB: // memset(objCode, '0', code->size * 2); // 目的碼為 0000…. objCode[code->size * 2] = '\0'; break; // 如果是 WORD: ``` ## OP_RESW OP_RESB 這邊的話 ,我們在D case 有看到我們的 > char format4[] = "%8x", format1[] = "%2x", *format = format1; > 程式進行到 OP_WORD or OP_BYTE的時候把格式切成 format4 然後在進行處理 我們的 args 可以看到我們的 裡面有 isdigit 可能是計算 到底有幾個 ![](https://i.imgur.com/MYglyOm.png) 加了幾行註解 發現 他是對 後面那個 args 進行轉為 16進位 也就是 29 to 1d 這邊有看到 他也可以填入 符號表意味者可以指定 特殊符號進行宣告? ![](https://i.imgur.com/MTaQNsI.png) 可以發現確實可以這樣操作。 ```C case OP_WORD: format = format4; // 設定輸出格式為 %8x case OP_BYTE: { // 如果是 BYTE : 輸出格式為 %2x Array *array = split(args, " ", REMOVE_SPLITER); // 其目的碼為每個數字轉為16進位的結果 char *objPtr = objCode; int i = 0; for (i = 0; i < array->count; i++) { char *item = array->item[i]; if (isdigit(item[0])) sprintf(objPtr, format, atoi(item)); else { AsmCode *itemCode = HashTableGet(a->symTable, item); sprintf(objPtr, format, itemCode->address); } objPtr += strlen(objPtr); } ArrayFree(array, strFree); break; } // case OP_BYTE: ``` # AsmSaveObjFile ... 下面的 函數就不加以討論可能大致上就是列印,然後儲存我們的目的碼或者是 計算我們的 varibale size 或 釋放記憶體 ```c void AsmSaveObjFile(Assembler *a, char *objFile) { printf("==========Save to ObjFile:%s==========\n", objFile); FILE *file = fopen(objFile, "wb"); int i; for (i=0; i<a->codes->count; i++) { AsmCode *code = a->codes->item[i]; char *objPtr = code->objCode; while (*objPtr != '\0') { int x; sscanf(objPtr, "%2x", &x); assert(x >= 0 && x < 256); BYTE b = (BYTE) x; fwrite(&b, sizeof(BYTE), 1, file); objPtr += 2; char bstr[3]; sprintf(bstr, "%2x", b); strReplace(bstr, " ", '0'); strToUpper(bstr); printf("%s", bstr); } } printf("\n"); fclose(file); } int AsmCodePrintln(AsmCode *code) { char label[100] = "", address[100], buffer[200]; if (strlen(code->label)>0) sprintf(label, "%s:", code->label); sprintf(address, "%4x", code->address); strReplace(address, " ", '0'); sprintf(buffer, "%s %-8s %-4s %-14s %c %2x %s\n", address, label, code->op, code->args, code->type, code->opCode, code->objCode); strToUpper(buffer); printf(buffer); } AsmCode* AsmCodeNew(char *line) { AsmCode* code = ObjNew(AsmCode,1); char label[100]="", op[100]="", args[100]="", temp[100]; int count = sscanf(line, "%s %s %[^;]", label, op, args); if (strTail(label, ":")) { strTrim(temp, label, ":"); strcpy(label, temp); } else { strcpy(label, ""); sscanf(line, "%s %[^;]", op, args); } // printf("label=%s op=%s args=%s\n", code->label, op, args); code->label = newStr(label); code->op = newStr(op); strTrim(temp, args, SPACE); code->args = newStr(temp); code->type = ' '; code->opCode = OP_NULL; // AsmCodePrintln(code); return code; } void AsmCodeFree(AsmCode *code) { freeMemory(code->label); freeMemory(code->op); freeMemory(code->args); freeMemory(code->objCode); freeMemory(code); } int AsmCodeSize(AsmCode *code) { // 計算指令的大小 switch (code->opCode) { // 根據運算碼 op case OP_RESW : // 如果是RESW return 4 * atoi(code->args); // 大小為 4*保留量 case OP_RESB : // 如果是RESB return atoi(code->args); // 大小為 1*保留量 case OP_WORD : // 如果是WORD return 4 * (strCountChar(code->args, ",")+1); // 大小為 4*參數個數 case OP_BYTE : // 如果是BYTE return strCountChar(code->args, ",")+1; // 大小為1*參數個數 case OP_NULL : // 如果只是標記 return 0; // 大小為 0 default : // 其他情形 (指令) return 4; // 大小為 4 } } ``` # 最終拓展 # OUTPUT ```C ===============Assemble============= LD R1, B ST R1, A CMP A,B JMP B RET B: WORD 29 C: WORD 10 A: RESW C =================PASS1================ 0000 LD R1, B L 0 (NULL) 0004 ST R1, A L 1 (NULL) 0008 CMP A,B A 10 (NULL) 000C JMP B J 26 (NULL) 0010 RET J 2C (NULL) 0014 B: WORD 29 D F2 (NULL) 0018 C: WORD 10 D F2 (NULL) 001C A: RESW C D F0 (NULL) ===============SYMBOL TABLE========= 001C A: RESW C D F0 (NULL) 0014 B: WORD 29 D F2 (NULL) 0018 C: WORD 10 D F2 (NULL) =============PASS2s============== 0000 LD R1, B L 0 00100000 0004 ST R1, A L 1 01100000 0008 CMP A,B A 10 10000000 000C JMP B J 26 26000004 0010 RET J 2C 2C000000 0014 B: WORD 29 D F2 0000001D 0018 C: WORD 10 D F2 0000000A 001C A: RESW C D F0 ==========Save to ObjFile:Ex4_1.obj0========== 001000000110000010000000260000042C0000000000001D0000000A ``` 一個組譯器就誕生了。。,這位老師還有 虛擬機 編譯器DEMO最近在來小分析一下。

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully