# 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)
###