透過前幾天加入的比較、迴圈的支援後,假設我們希望在 TFT 螢幕上繪製文字跑馬燈就必須處理字串,也就是能將字串讀取進來。不過在這之前我們需要將 mrb_value
完善,前面的實作我們都是利用 0
和 1
來區分 true
和 false
的,但實際上我們處理的都應該要是 mrb_value
才對。
這就牽涉到我們目前使用的 reg[]
陣列,因為他現在是以 intptr_t
來儲存的,所以我們可以拿來當作整數值或者作為指標指向某個記憶體位置,但是在 mruby 的實作中,它實際上是一個 mrb_value
陣列,而我們所有的操作都是 mrb_value
的替換(複製)
也因此,當我們抓取到一個 mrb_value
的時候,就能夠透過 tt
資訊判斷是否為字串,進而讀取 value.p
而非 value.i
來獲取指標並且當作 char*
讀取來當作字串。
reg
實作// lib/iron/vm.c
// ...
mrb_value stack[irep->nregs - 1];
// ...
// Initialize
stack[0] = mrb_nil_value();
// ...
我們先將 reg
改名為 stack
並且修改型別為 mrb_value
而這也是在 mruby 實際上使用的名字,同時我們將初始化的 stack[0]
設定為 nil
的數值,因為對我們來說我們並沒有物件的概念,也就不會有 Kernel
物件可以作為參考。
推測會叫做
stack
的原因應該是因為 Ruby 都是 Stack-based VM 為基礎所設計的關係,除此之外之前提過的 Context 也都會有自己的 Stack 存在,有點類似當下呼叫的狀況所擁有的資訊堆疊。
vm.c
因為修改的部分相當零碎,這邊直接將程式碼片段貼上
// lib/iron/vm.c
// ...
while(!error) {
uint8_t insn = READ_B();
switch(insn) {
CASE(OP_MOVE, BB){
stack[a] = stack[b];
DEBUG_LOG("r[%d] = r[%d] : %p", a, b, &stack[b]);
NEXT;
}
CASE(OP_LOADI, BB) {
stack[a] = mrb_fixnum_value(b);
DEBUG_LOG("r[%d] = %d", a, b);
NEXT;
}
CASE(OP_LOADINEG, BB) {
stack[a] = mrb_fixnum_value(-b);
DEBUG_LOG("r[%d] = %d", a, -b);
NEXT;
}
CASE(OP_LOADI__1, B) goto L_LOADI;
CASE(OP_LOADI_0, B) goto L_LOADI;
CASE(OP_LOADI_1, B) goto L_LOADI;
CASE(OP_LOADI_2, B) goto L_LOADI;
CASE(OP_LOADI_3, B) goto L_LOADI;
CASE(OP_LOADI_4, B) goto L_LOADI;
CASE(OP_LOADI_5, B) goto L_LOADI;
CASE(OP_LOADI_6, B) goto L_LOADI;
CASE(OP_LOADI_7, B) {
L_LOADI:
stack[a] = mrb_fixnum_value(insn - OP_LOADI_0);
DEBUG_LOG("r[%d] = mrb_int(%d)", a, mrb_int(stack[a]));
NEXT;
}
CASE(OP_LOADNIL, B) {
stack[a] = mrb_nil_value();
DEBUG_LOG("r[%d] = nil", a);
NEXT;
}
CASE(OP_LOADSELF, B) {
stack[a] = stack[0];
DEBUG_LOG("r[%d] = self %p", a, &stack[a]);
NEXT;
}
CASE(OP_LOADT, B) goto L_LOADF;
CASE(OP_LOADF, B) {
L_LOADF:
stack[a] = insn == OP_LOADT ? mrb_true_value() : mrb_false_value();
DEBUG_LOG("r[%d] = %s", a, insn == OP_LOADT ? "true" : "false");
NEXT;
}
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] == %s", b, a, !MRB_IS_FALSE(stack[a]) ? "true" : "false");
} else {
DEBUG_LOG("jmp %d if !r[%d] == %s", b, a, MRB_IS_FALSE(stack[a]) ? "false" : "true");
}
if ((insn == OP_JMPIF) == (!MRB_IS_FALSE(stack[a]))) {
p = porg + b;
}
NEXT;
}
CASE(OP_ADDI, BB) {
stack[a] = mrb_fixnum_value(mrb_fixnum(stack[a]) + b);
DEBUG_LOG("r[%d] = r[%d] + %d", a, a, b);
NEXT;
}
CASE(OP_EQ, B) {
stack[a] = mrb_fixnum(stack[a]) == mrb_fixnum(stack[a + 1]) ? mrb_true_value() : mrb_false_value();
DEBUG_LOG("r[%d] = r[%d] == r[%d+1]", a, a, a);
NEXT;
}
CASE(OP_LT, B) {
stack[a] = mrb_fixnum(stack[a]) < mrb_fixnum(stack[a + 1]) ? mrb_true_value() : mrb_false_value();
DEBUG_LOG("r[%d] = r[%d] < r[%d+1]", a, a, a);
NEXT;
}
CASE(OP_LE, B) {
stack[a] = mrb_fixnum(stack[a]) <= mrb_fixnum(stack[a + 1]) ? mrb_true_value() : mrb_false_value();
DEBUG_LOG("r[%d] = r[%d] <= r[%d+1]", a, a, a);
NEXT;
}
CASE(OP_GT, B) {
stack[a] = mrb_fixnum(stack[a]) > mrb_fixnum(stack[a + 1]) ? mrb_true_value() : mrb_false_value();
DEBUG_LOG("r[%d] = r[%d] > r[%d+1]", a, a, a);
NEXT;
}
CASE(OP_GE, B) {
stack[a] = mrb_fixnum(stack[a]) >= mrb_fixnum(stack[a + 1]) ? mrb_true_value() : mrb_false_value();
DEBUG_LOG("r[%d] = r[%d] > r[%d+1]", a, a, a);
NEXT;
}
CASE(OP_SEND, BBB) {
const char* name = (const char*)irep_get(data, IREP_TYPE_SYMBOL, b);
khiter_t key = kh_get(mt, mrb->mt, name);
if (key == kh_end(mrb->mt)) {
DEBUG_LOG("func %s not found", name);
} else {
DEBUG_LOG("func = %s", name);
mrb_func_t func = kh_value(mrb->mt, key);
mrb_value argv[c];
for(int i = 1; i <= c; i++) {
argv[i - 1] = stack[a + i];
}
mrb_callinfo ci = { .argc = c, .argv = argv };
mrb->ci = &ci;
func(mrb);
}
NEXT;
}
CASE(OP_RETURN, B) {
DEBUG_LOG("%s r[%d]", insn == OP_RETURN ? "return" : "break", a);
return mrb_fixnum(stack[a]);
}
default:
DEBUG_LOG("Unsupport OP Code: %d\n", insn);
error = 1;
}
}
// ...
基本上就是將數字相關的用 mrb_fixnum_value
製作出 mrb_value
類型的資料,如果是比較的處理則修改為 mrb_fixnum
取出後處理,再用 mrb_fixnum_value
製作新的 mrb_value
放回去,除此之外就是將除錯訊息調整成能被讀取到的狀態,因為我們原版是以 intptr_t
的方式儲存可以直接當作整數輸出,但是現在我們需要用像是指標之類的方式以記憶體位置的方式呈現。
除此之外我們在 lib/iron/value.h
加入了 MRB_IS_FALSE
巨集來幫助我們判斷數值是否為 false
或者 nil
// lib/iron/value.h
// ...
#define MRB_IS_FALSE(o) (o.tt == MRB_TT_FALSE)
//...
修改完畢後可以用 pio test
驗證是否能正常運作,下一篇我們會來製作可以將字串儲存到 mrb_value
的處理,讓我們可以支援輸出字串的功能。