經過將近一個月的努力,現在我們現在要擴充 OPCode 的處理也容易很多,階段性的目標是要能在 TFT 螢幕上顯示一些訊息跟動畫,因此除了前面能讓 Ruby 運作起來我們還需要一些像是邏輯判斷的機能來幫助我們做處理。
更新 lib/iron/opcode.h
增加比較相關的指令
// lib/iron/opcode.h
enum {
// ...
OP_EQ = 65,
OP_LT,
OP_LE,
OP_GT,
OP_GE,
// ...
};
更新 lib/iron/vm.c
增加對應的處理
// lib/iron/vm.c
// ...
CASE(OP_EQ, B) {
reg[a] = reg[a] == reg[a + 1] ? 1 : 0;
DEBUG_LOG("r[%d] = r[%d] == r[%d+1]", a, a, a);
NEXT;
}
CASE(OP_LT, B) {
reg[a] = reg[a] < reg[a + 1] ? 1 : 0;
DEBUG_LOG("r[%d] = r[%d] < r[%d+1]", a, a, a);
NEXT;
}
CASE(OP_LE, B) {
reg[a] = reg[a] <= reg[a + 1] ? 1 : 0;
DEBUG_LOG("r[%d] = r[%d] <= r[%d+1]", a, a, a);
NEXT;
}
CASE(OP_GT, B) {
reg[a] = reg[a] > reg[a + 1] ? 1 : 0;
DEBUG_LOG("r[%d] = r[%d] > r[%d+1]", a, a, a);
NEXT;
}
CASE(OP_GE, B) {
reg[a] = reg[a] >= reg[a + 1] ? 1 : 0;
DEBUG_LOG("r[%d] = r[%d] > r[%d+1]", a, a, a);
NEXT;
}
// ...
這樣我們就能夠根據 Ruby VM 裡面的寄存器所儲存的數值來做比較,並且用 1
和 0
表示 true
和 false
既然能夠比較數值,我們就還需要能夠進行判斷的 if
語法支援,繼續對 OPCode 擴充
// lib/iron/opecode.h
enum {
// ...
OP_JMP = 33,
OP_JMPIF,
OP_JMPNOT,
OP_JMPNIL,
// ...
};
在 mruby 的處理中,當判斷成立或者不成立的時候會「跳」到指定的位置執行,假設資料是一個陣列的話
[
OP, # LOADI
DATA,
OP, // LOADI
DATA,
OP, // IF -> 跳到 A (true) 或 B (false)
DATA,
DATA,
OP, // IF true (A)
DATA,
OP,
DATA,
OP, // IF false (A)
DATA,
OP,
DATA
]
透過這樣的方式也能夠實現迴圈之類的效果,因此我們要實作的行為會是 JMP
、JMPIF
這類邏輯。
// lib/iron/vm.c
// 跳躍點紀錄
const uint8_t* prog = p;
while(!error) {
// ...
CASE(OP_JMP, S) {
p = porg + a;
DEBUG_LOG("jmp %d", b);
NEXT;
}
CASE(OP_JMPIF, BS) goto L_JMPNOT;
CASE(OP_JMPNOT, BS) {
L_JMPNOT:
if (insn == OP_JMPIF) {
DEBUG_LOG("jmp %d if r[%d] == %ld", b, a, reg[a]);
} else {
DEBUG_LOG("jmp %d if !r[%d] == %ld", b, a, reg[a]);
}
if ((insn == OP_JMPIF) == (reg[a] != 0)) {
p = porg + b;
}
NEXT;
}
// ...
}
這邊稍為不同的是要「跳躍」的目標我們在之前的實作是無法得知的,因此需要增加一個 prog
來記錄 ISEQ 的起始點,用這個數值當作參考紀錄 mruby 告訴我們要將處理的位置移動到哪裡繼續執行。
因為這次我們抓取的數值不太一樣,因此要在 lib/iron/opcode.h
另外增加 FETCH_
系列的巨集來輔助我們做處理。
// lib/iron/opcode.h
// ...
#define FETCH_BS() do { a = READ_B(); b = READ_S(); } while(0)
#define FETCH_S() do { a = READ_S(); } while(0)
// ...
稍微修改 test/test_native/main.cpp
加入 puts
實作來做測試
// test/test_native/main.cpp
void mrb_puts(mrb_state* mrb) {
int argc = mrb_get_argc(mrb);
mrb_value* argv = mrb_get_argv(mrb);
for(int i = 0; i < argc; i++) {
printf("%d\n", mrb_fixnum(argv[i]));
}
}
void test_mrb_run(void) {
mrb_state* mrb = mrb_open();
mrb_define_method(mrb, "puts", mrb_puts);
mrb_run(mrb, app);
mrb_close(mrb);
}
// ...
更新 app.rb
來加入可以做 if
判斷的處理
puts 1 if 10 > 5
最後我們就可以用 pio test
來驗證我們的實作是否正確。
下一篇我們會實作迴圈的機制,讓我們的程式可以維持運行,這樣就能讓我們在 TFT 螢幕上呈現一些動畫。