Задумал я тут ВМ написать (под скриптовый DSL для определенных нужд)

Идея простая как бревно. Есть универсальный интерфейс Value для любых значений.
Значениями могут быть числа с плавающей точкой, строки, объекты etc. (в примере только числа)
Машина предполагается регистровая. Каждая инструкция 4 байта.
Я написал простой тестовый пример. Надеюсь по коду понятно что происходит.

Это я руками сделал тупую компиляцию следующего:
## Код:
```
x = 0
y = 1
while x <= 100000000 do
   x = x + y
end
```


Как улучшить следующий подход?
Можно ли избежать лишней работы с кучей не прибегая к union?

Больше всего меня интересует скорость исполнения байткода. Этот пример у меня выполняется 6 сек., что слишком долго.

ps Просьба не обращать внимание на стиль, т.к. написано левой пяткой только ради эксперимента.

## Код:
```
MODULE MyTest;
IMPORT Log := StdLog;

CONST
   Add = 0; Set = 1; Get = 2; Jmp = 3; Jgt = 4; End = 5;

TYPE

Value = POINTER TO EXTENSIBLE RECORD END;

Number = POINTER TO RECORD(Value)
   r: REAL;
END;

PROCEDURE (v: Value) Num(): REAL, NEW, EXTENSIBLE;
BEGIN
RETURN 0;
END Num;

PROCEDURE (v: Value) SetReal(x: REAL), NEW, EXTENSIBLE;
BEGIN
END SetReal;

PROCEDURE (v: Value) Set(x: Value), NEW, EXTENSIBLE;
BEGIN
END Set;

PROCEDURE (v: Number) Num(): REAL;
BEGIN
RETURN v.r;
END Num;

PROCEDURE (v: Number) SetReal(x: REAL);
BEGIN
   v.r := x;
END SetReal;

PROCEDURE (v: Number) Set(x: Value);
BEGIN
   v.r := x.Num();
END Set;

PROCEDURE emit1(b1, b2, b3, b4: INTEGER): INTEGER;
   VAR w: INTEGER;
BEGIN
   w := ASH(b1, 24) + ASH(b2, 16) + ASH(b3, 8) + b4;
   RETURN w;
END emit1;

PROCEDURE emit2(b1, b2, b4: INTEGER): INTEGER;
   VAR w: INTEGER;
BEGIN
   w := ASH(b1, 24) + ASH(b2, 16) + b4;
   RETURN w;
END emit2;

PROCEDURE emit3(b1, b4: INTEGER): INTEGER;
   VAR w: INTEGER;
BEGIN
   w := ASH(b1, 24) + b4;
   RETURN w;
END emit3;

PROCEDURE emit4(b1: INTEGER): INTEGER;
   VAR w: INTEGER;
BEGIN
   w := ASH(b1, 24);
   RETURN w;
END emit4;

PROCEDURE Do*;
   VAR w: INTEGER;
   b1, b2, b3, b4: INTEGER;
   ip: INTEGER;
   ops: ARRAY 13 OF INTEGER;
   reg: ARRAY 256 OF Value;
   mem: ARRAY 1000 OF Value;
   r: REAL;
   n: Number;
   i: INTEGER;
BEGIN
   
   (* помещение программы в память кода *)

   ops[00] := emit2(Get, 0, 0);    (* 0 *)
   ops[01] := emit2(Set, 0, 10);   (* x = 0 *)
   ops[02] := emit2(Get, 0, 1);    (* 1 *)
   ops[03] := emit2(Set, 0, 11);   (* y = 1 *)
   ops[04] := emit2(Get, 0, 10);   (* x *)
   ops[05] := emit2(Get, 1, 2);    (* 100000000 *)
   ops[06] := emit3(Jgt, 11);      (* if x > 100000000 goto *)
   ops[07] := emit2(Get, 0, 10);   (* x *)
   ops[08] := emit2(Get, 1, 11);   (* y *)
   ops[09] := emit1(Add, 0, 0, 1); (* _ = x + y *)
   ops[10] := emit2(Set, 0, 10);   (* x = _ *)
   ops[11] := emit3(Jmp, 3);       (* goto *)
   ops[12] := emit4(End);          (* end *)
   
   (* подготовка регистров *)
   
   FOR i := 0 TO LEN(reg) - 1 DO
      NEW(n); n.r := 0;
      reg[i] := n;
   END;
   
   (* подготовка памяти данных *)
   
   FOR i := 0 TO 20 DO
      NEW(n); n.r := 0;
      mem[i] := n;
   END;
   
   (* размещение констант в памяти *)

   NEW(n); n.r := 0;
   mem[0] := n;
   
   NEW(n); n.r := 1;
   mem[1] := n;
   
   NEW(n); n.r := 100000000;
   mem[2] := n;
   
   (* цикл ВМ *)

   ip := 0;
   
   i := 0;
   LOOP
      w := ops[ip];
      
      b4 := w MOD 0100H;
      b3 := w DIV 0100H MOD 0100H;
      b2 := w DIV 010000H MOD 0100H;
      b1 := w DIV 01000000H MOD 0100H;
         
      CASE b1 OF
      |Get: reg[b2].Set(mem[b4]);
      |Set: mem[b4].Set(reg[b2]); 
      |Add: reg[b2].SetReal(reg[b3].Num() + reg[b4].Num());
      |Jmp: ip := b4;
      |Jgt: IF reg[0].Num() > reg[1].Num() THEN ip := b4 END;
      |End: EXIT;
      END;
      INC(ip);
      
   END;
   
END Do;

END MyTest.Do

DevCompiler.CompileThis MyTest!!
DevDebug.UnloadThis MyTest
```
