Try   HackMD

2017q3 Homework3 (simulator)

tags: sysprog2017

contributed by <workfunction>

github repository

新增OP_SCAN

實作Fibonacci時需要使用者輸入要計算第幾項,因此需要一個可以輸入的介面,目前想到兩種方法實做:組譯器執行時加入引述或是新增一個輸入指令,我選擇後者實做。

Github commit

in opcode.c

static void scan_impl(VM_HANDLER_ARGS) { scanf("%d", &(VM_INT(op1))); VM_T(op1) = INT; }

簡單新增一個 scanf 的功能

in opcode.def

name, opcode, has_op1, has_op2, has_result ... scan, 23, 1, 0, 0 ...

定義 opcode 23為 scan

in vm.c

void vm_run(vm_env *env) { ... OP(SCAN) : VM_CALL_HANDLER(); ... }

vm_run 中加入 scan

新增 Label 支援

in driver.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

Label資料儲存型別

in vm.h

typedef struct { char *label; int address; } vm_label;

新增一個結構儲存 label 的位置

in vm.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

    ​​​​switch (data[0]) { ​​​​... ​​​​ case ':': ​​​​ op.type = LABEL; ​​​​ op.label = strdup(data) + 1; ​​​​ break; ​​​​... ​​​​}

    判斷如果 operand 是以 : 為開頭,其後面為 label

    in vm.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

    ​​​​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

    ​​​​+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

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

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