# 2017q3 Homework3 (simulator) tags: `sysprog2017` contributed by <`workfunction`> [github repository](https://github.com/workfunction/full-stack-hello) ## 新增OP_SCAN 實作Fibonacci時需要使用者輸入要計算第幾項,因此需要一個可以輸入的介面,目前想到兩種方法實做:組譯器執行時加入引述或是新增一個輸入指令,我選擇後者實做。 [Github commit](https://github.com/workfunction/full-stack-hello/commit/7e21e5b930ff2d87f75075543bcf05c5e52f02c0) in `opcode.c` ```c= static void scan_impl(VM_HANDLER_ARGS) { scanf("%d", &(VM_INT(op1))); VM_T(op1) = INT; } ``` 簡單新增一個 `scanf` 的功能 in `opcode.def` ```css= name, opcode, has_op1, has_op2, has_result ... scan, 23, 1, 0, 0 ... ``` 定義 `opcode` 23為 scan in `vm.c` ```c= void vm_run(vm_env *env) { ... OP(SCAN) : VM_CALL_HANDLER(); ... } ``` 在 `vm_run` 中加入 scan ## 新增 Label 支援 in `driver.c` ```c= case ASSEMBLE_AND_EVAL: { vm_env *env = vm_new(); assemble_from_fd(env, in_fd); hook_opcodes(env); vm_run(env); vm_free(env); break; } ``` 在程式執行期間操作如下: - `assemble_from_fd` 將檔案掃描過一遍並將指令儲存成 struct table - `hook_opcodes` 將 OP code 與實做連接 - `vm_run` 將指令 struct table 執行 而要達成 label 的支援需要: - 在 `assemble_from_fd` 中新增能夠判斷 label 的機制 - 在 `vm_run` 前將 label 轉換成絕對位置,如此就不必修改分之類指令的行為 [Github commit](https://github.com/workfunction/full-stack-hello/commit/59ab4845a36ef8b2f5b5a9122e2602860525f6e1) ### Label資料儲存型別 in `vm.h` ```c= typedef struct { char *label; int address; } vm_label; ``` 新增一個結構儲存 label 的位置 in `vm.c` ```c= struct __vm_env { ... vm_label labels[LABELS_MAX_SIZE]; /* Labels table */ vm_operand *label_reference[LABELS_MAX_SIZE]; /* Refrenced Labels */ int labels_count; int label_reference_count; }; ``` 在 `env` 中加入 label table 將所有 label 與位置紀錄在這裡,並且用 vm_operand 指標 table `label_reference` 將所有被 refrenced 的 label 的 operand 位置記錄在這裡 ### 判斷並儲存 label 機制 - 儲存指令參照過的 label in `as.c` ```c= switch (data[0]) { ... case ':': op.type = LABEL; op.label = strdup(data) + 1; break; ... } ``` 判斷如果 operand 是以 `:` 為開頭,其後面為 label in `vm.c` ```c= size_t vm_add_inst(vm_env *env, vm_inst inst) { env->insts[env->insts_count] = inst; if (inst.op1.type == LABEL) env->label_reference[env->label_reference_count++] = &env->insts[env->insts_count].op1; if (inst.op2.type == LABEL) env->label_reference[env->label_reference_count++] = &env->insts[env->insts_count].op2; return env->insts_count++; } ``` 如果指令後面的 operand 被標記成 label,將其儲存至 `label_reference` ,之後再轉換成絕對位置 - 儲存 label 位置 in `as.c` ```c= static void assemble_line(vm_env *env, char *line) { ... const struct instruction *inst = find_inst(mnemonic); if (!inst) { // checking label int mlen = strlen(mnemonic); if (mlen > 1 && mnemonic[mlen - 1] == ':') { mnemonic[mlen - 1] = '\0'; vm_add_label(env, mnemonic); free(line_backup); return; } FATALX(1, "instruction `%s' not found\n", mnemonic); } ``` in `vm.c` ```c= +size_t vm_add_label(vm_env *env, char *label) { env->labels[env->labels_count].label = strdup(label); env->labels[env->labels_count].address = env->insts_count; return env->labels_count++; } ``` 如果該行指令並非系統預留指令並以 `:` 結尾將其定義為 label 並存至 label table ### 將 label 轉換成絕對位置 in `driver.c` ```c= case ASSEMBLE_AND_EVAL: { vm_env *env = vm_new(); assemble_from_fd(env, in_fd); hook_opcodes(env); vm_map_refrenced_label(env); vm_run(env); vm_free(env); break; } ``` in `vm.c` ```c= void vm_map_refrenced_label(vm_env *env) { for (int i = 0; i < env->label_reference_count; ++i) { env->label_reference[i]->value.id = vm_find_label(env, env->label_reference[i]->label); } } ``` 在執行前新增一個 `vm_map_refrenced_label` function 將所有指令中出現的 label (`label_reference`) 依據之前存的 label table 把位置一一放入 `vm_operand.value.id` 中,如此便能相容原本分之類指令使用 `vm_operand.value.id` 儲存絕對位置 ## Fibonacci 數列的實作 ### Recursive, Iterative, Tail recursive [Github commit](https://github.com/workfunction/full-stack-hello/commit/85c6cd8a8024fdb600f48518040cfceb05bf05f0) ###