Exemplo n.º 1
0
        public void LocalsSegmentTest()
        {
            var(o1, o2) = new EvaluationSegment(0xAB, "f64").OpCode;
            var eva = EvaluationSegment.Construct((ulong)o1.Assembly(), (ulong)o2.Assembly());

            Assert.Equal("f64", eva.Type);
            Assert.Equal(0xAB, eva.Index);
        }
Exemplo n.º 2
0
        public void Eval()
        {
            MemoryManagement.FastWrite = fw;
            if (bf)
            {
                bus.debugger.handleBreak(u16 & pc, this);
                mem[0x17] = 0x0;
            }

            switch (iid)
            {
            case 0x0:
                trace("call :: skip");
                break;

            case { } opcode when opcode.In(0xD0..0xE8):
            {
                /* need @float-flag */
                if (!ff)
                {
                    bus.cpu.halt(0xA9);
                }
                trace($"call :: [0xD0..0xE8]::0x{iid:X} [0x{pipe.arg1:X}] [0x{pipe.arg2:X}] with 0x{x3:X} mode");
                var result = iid switch
                {         // todo refactoring
                    0xD0 => f32u64& Abs(u64f32& pipe[0x1]),
                    0xD1 => f32u64& Acos(u64f32& pipe[0x1]),
                    0xD2 => f32u64& Atan(u64f32& pipe[0x1]),
                    0xD3 => f32u64& Acosh(u64f32& pipe[0x1]),
                    0xD4 => f32u64& Atanh(u64f32& pipe[0x1]),
                    0xD5 => f32u64& Asin(u64f32& pipe[0x1]),
                    0xD6 => f32u64& Asinh(u64f32& pipe[0x1]),
                    0xD7 => f32u64& Cbrt(u64f32& pipe[0x1]),
                    0xD8 => f32u64& Ceiling(u64f32& pipe[0x1]),
                    0xD9 => f32u64& Cos(u64f32& pipe[0x1]),
                    0xDA => f32u64& Cosh(u64f32& pipe[0x1]),

                    0xDB => f32u64& Floor(u64f32& pipe[0x1]),
                    0xDC => f32u64& Exp(u64f32& pipe[0x1]),
                    0xDD => f32u64& Log(u64f32& pipe[0x1]),
                    0xDE => f32u64& Log10(u64f32& pipe[0x1]),
                    0xDF => f32u64& Tan(u64f32& pipe[0x1]),
                    0xE0 => f32u64& Tanh(u64f32& pipe[0x1]),

                    0xE4 => f32u64& Atan2(u64f32& pipe[0x1], u64f32& pipe[0x2]),
                    0xE5 => f32u64& Min(u64f32& pipe[0x1], u64f32& pipe[0x2]),
                    0xE6 => f32u64& Max(u64f32& pipe[0x1], u64f32& pipe[0x2]),

                    0xE7 => f32u64& Sin(u64f32& pipe[0x1]),
                    0xE8 => f32u64& Sinh(u64f32& pipe[0x1]),

                    0xE1 => f32u64& Truncate(u64f32& pipe[0x1]),
                    0xE2 => f32u64& BitDecrement(u64f32& pipe[0x1]),
                    0xE3 => f32u64& BitIncrement(u64f32& pipe[0x1]),
                    _ => throw new CorruptedMemoryException("")
                };
                pipe[0x3] = result;
                break;
            }
                #region halt

            case 0xF when new[] { r1, r2, r3, u1, u2, x1 }.All(x => x == 0xF):
                bus.cpu.halt(0xF);
                break;

            case 0xD when r1 == 0xE && r2 == 0xA && r3 == 0xD:
                bus.cpu.halt(0x0);
                break;

            case 0xB when r1 == 0x0 && r2 == 0x0 && r3 == 0xB && u1 == 0x5:
                bus.cpu.halt(0x1);
                break;

                #endregion halt

            case 0x1 when x2 == 0x0:
                trace($"call :: ldi 0x{u1:X}, 0x{u2:X} -> 0x{r1:X}");
                _ = u2 switch
                {
                    0x0 => mem[r1] = u1,
                    _ => mem[r1]   = i64 | ((u2 << 4) | u1)
                };
                break;

            case 0x1 when x2 == 0xA:
                trace($"call :: ldx 0x{u1:X}, 0x{u2:X} -> 0x{r1:X}-0x{r2:X}");
                mem[((r1 << 4) | r2)] = i64 | ((u1 << 4) | u2);
                break;

            case 0x3:     /* @swap */
                trace($"call :: swap, 0x{r1:X}, 0x{r2:X}");
                mem[r1] ^= mem[r2];
                mem[r2]  = mem[r1] ^ mem[r2];
                mem[r1] ^= mem[r2];
                break;

            case 0xF when x2 == 0xE:     // 0xF9988E0
                trace($"call :: move, dev[0x{r1:X}] -> 0x{r2:X} -> 0x{u1:X}");
                bus.find(r1 & 0xFF).write(r2 & 0xFF, i32 & mem[u1] & 0xFF);
                break;

            case 0xF when x2 == 0xC:     // 0xF00000C
                trace($"call :: move, dev[0x{r1:X}] -> 0x{r2:X} -> [0x{u1:X}-0x{u2:X}]");
                bus.find(r1 & 0xFF).write(r2 & 0xFF, (r3 << 12 | u1 << 8 | u2 << 4 | x1) & 0xFFFFFFF);
                break;

            case 0xA3:     /* wtd */
                trace($"call :: wtd dev[0x{r1:X}{r2:X}] -> 0x{u1:X}{u2:X}");
                bus.find((d8u)(u8 & r1, u8 & r2)).write((d8u)(u8 & u1, u8 & u2), (d8u)(u8 & x1, u8 & x2));
                stack.push(bus.find(r1 & 0xFF).read(r2 & 0xFF));
                break;

            case 0xA4:     /* @rfd */
                trace($"call :: rfd dev[0x{r1:X}{r2:X}] -> 0x{u1:X}{u2:X}");
                stack.push(bus.find((d8u)(u8 & r1, u8 & r2)).read((d8u)(u8 & u1, u8 & u2)));
                break;

            case 0x8 when u2 == 0xC:     /* @ref.t */
                trace($"call :: ref.t 0x{r1:X}");
                mem[r1] = pc;
                break;

            case 0xA0:
                trace($"call :: orb '{r1}' times");
                for (var i = pc + r1; pc != i;)
                {
                    stack.push(fetch());
                }
                break;

            case 0xA1:
                trace($"call :: pull -> 0x{r1:X}");
                mem[r1] = stack.pop();
                break;

            case 0xA5:     /* @sig */
                d8u arg_count  = (u8 & r1, u8 & r2);
                var returnType = ExternType.Find(
                    (u8 & r3, u8 & u1),
                    (u8 & u2, u8 & x1),
                    (u8 & x2, u8 & x3),
                    (u8 & x4, u8 & o1)
                    );
                // read function name
                var lp = (fetch() & 0x0000_0000_FFFF_FFFF_0000) >> 12 >> 4;
                var p  = StringLiteralMap.GetInternedString((int)lp);
                NativeString.Unwrap(p,
                                    out var functionName, false, true);

                // read argument declaration
                var args = new Utb[arg_count];
                for (var i = 0; i != arg_count; i++)
                {
                    args[i] = (ExternType.FindAndConstruct(fetch()), 0);
                }

                var memory = bus.find(0x0) as Memory;

                Debug.Assert(memory != null, $"{nameof(memory)} != null");

                // write function into memory
                var(free, startPoint) = memory.GetFreeAddress();

                memory.writeString(ref free, Module.Current.Name);
                memory.writeString(ref free, functionName);

                memory.write(free++, arg_count);
                foreach (var arg in args)
                {
                    memory.writeString(ref free, arg.ConstructType().ShortName);
                    memory.write(free++, arg.Value);
                }
                memory.writeString(ref free, returnType.ShortName);


                var bodyStart = free;

                var n_frag = fetch();
                while (AcceptOpCode(n_frag) != 0xA6 /* @ret */)
                {
                    memory.write(free++, n_frag);
                    n_frag = fetch();
                }
                memory.write(free++, n_frag);

                VMRef metadataRef = (startPoint, free - startPoint);
                VMRef bodyRef     = (bodyStart, free - bodyStart);

                trace($"call :: declare function [{functionName}() -> {returnType.ShortName}] ");

                Module.Current.RegisterFunction(new Function((metadataRef, bodyRef), memory));
                break;

            case 0xA6:     /* @ret */
                pc = cr.Recoil();
                CallStack.Exit();
                trace("call :: ret");
                break;

            case 0x36:     /* @call.i */
                cr.Branch(pc);
                d32i fp = (
                    u8 & r1, u8 & r2,
                    u8 & r3, u8 & u1,
                    u8 & u2, u8 & x1,
                    u8 & x2, u8 & x3
                    );
                var f = Module.Current.FindFunction(fp);
                trace($"call :: call.i !{{{f.Name}}}() -> {f.ReturnType.ShortName}");
                CallStack.Enter(f, pc);
                pc = f.GetCoilRef().Point;
                break;

            case 0x41:
                d32i mn = (
                    u8 & r1, u8 & r2,
                    u8 & r3, u8 & u1,
                    u8 & u2, u8 & x1,
                    u8 & x2, u8 & x3
                    );
                NativeString.Unwrap(StringLiteralMap.GetInternedString(mn),
                                    out var modulePath, false, true);
                trace($"call :: use '{modulePath}'");
                Module.Import(modulePath);
                break;

            case 0xB1:     /* @inc */
                trace($"call :: increment [0x{r1:X}]++");
                if (!ff)
                {
                    unchecked { mem[r1]++; }
                }
                else
                {
                    mem[r1] = f32u64 & ((u64f32 & mem[r1]) + 1f);
                }
                break;

            case 0xB2:      /* @dec */
                trace($"call :: decrement [0x{r1:X}]--");
                if (!ff)
                {
                    unchecked { mem[r1]--; }
                }
                else
                {
                    mem[r1] = f32u64 & ((u64f32 & mem[r1]) - 1f);
                }
                break;

            case 0xB3:     /* @dup */
                trace($"call :: dup 0x{(u1 << 4) | u2:X}");
                mem[(u1 << 4) | u2] = mem[(r1 << 4) | r2];
                break;

            case 0xB4:     /* @ckft */
                trace($"call :: ckft 0x{(r1 << 4) | r2:X}");
                if (ff && !float.IsFinite(u64f32 & mem[(r1 << 4) | r2]))
                {
                    bus.cpu.halt(0xA9);
                }
                break;

            case 0x40:     /* @__static_extern_call */
                d16u sign = (
                    u8 & r1, u8 & r2,
                    u8 & r3, u8 & u1
                    );
                trace($"call :: static_call 0x{sign.Value:X}");
                var find = Module.Context.Find(sign, out var @extern);
                if (find != ExternStatus.Found)
                {
                    bus.cpu.halt(0xA16 + (int)find, $"0x{sign.Value:X}");
                    return;
                }
                trace($"call :: {@extern.Signature}");
                CallAndWrite(@extern);
                break;

            case 0x37:     /* @prune */
                evaluation.Prune();
                break;

            case 0x38:     /* @locals */
                d8u len = (u8 & r1, u8 & r2);
                evaluation.Alloc(len);
                var segs = new List <EvaluationSegment>(len);
                for (var i = 0; i != len; i++)
                {
                    segs.Add(EvaluationSegment.Construct(fetch(), fetch()));
                }
                var @params = new object[len];
                foreach (var s in segs)
                {
                    @params[s.Index] = ExternType.Find(s.Type);
                }
                locals.Pin(@params, out var @external);
                foreach (var(host, e_index) in @external.Select((x, i) => (x, i)))
                {
                    evaluation[e_index] = host;
                }
                break;

            case 0x39:     /* @readonly */
                break;

            /*
             * case 0xB5: / @ixor /
             * case 0xB6: / @ior /
             *
             *  d8u first = (u8 & r1, u8 & r2);
             *  d8u second = (u8 & u1, u8 & u2);
             *  // not support float-mode
             *  if (ff) bus.cpu.halt(0xA9);
             *  if (iid == 0xB5)
             *      mem[first] ^= mem[second];
             *  if (iid == 0xB6)
             *      mem[first] |= mem[second];
             *  break;
             */
            case 0xB5:     /* @neg */
                d8u c1 = (u8 & r1, u8 & r2);
                trace($"call :: neg [0x{(ushort)c1:X}]");
                if (ff)
                {
                    mem[c1] = f32u64 & ((u64f32 & mem[c1]) * -1f);
                }
                else
                {
                    mem[c1] = (ulong)((long)mem[c1] * -1);
                }
                break;

            case 0x34:     /* @lpstr */
                d32u str_index = (u8 & r1, u8 & r2, u8 & r3, u8 & u1, u8 & u2, u8 & x1, u8 & x2, u8 & x3);
                if (!StringLiteralMap.Has(str_index))
                {
                    bus.cpu.halt(0xDE3, $"index {str_index.Value} not found in memory");
                }
                stack.push(str_index.Value);
                break;

            case 0x35:     /* @unlock */
                d8u unlock_cell = (u8 & r1, u8 & r2);
                mem[unlock_cell]       = stack.pop();
                mem_types[unlock_cell] = ExternType.Find(
                    (u8 & r3, u8 & u1),
                    (u8 & u2, u8 & x1),
                    (u8 & x2, u8 & x3)
                    );
                break;

            case 0xC2:     /* @dif_t */
            case 0xC3:     /* @dif_f */
                d8u target = (u8 & r1, u8 & r2);
                trace($"call :: dif [0x{target:X}]");
                if (!(mem_types[target] is i2_Type))
                {
                    bus.cpu.halt(0xA22, $"[0x{target:X}] is {mem_types[pipe.arg1].GetType().Name}");
                }
                d8u skip = (u8 & r3, u8 & u1);
                if (mem[target] == (iid == 0xC2ul ? 0x1ul : 0x0ul))
                {
                    pc += skip;
                }
                break;

            case 0xC5:     /* @ceq */
                trace($"call :: ceq [0x{pipe.arg1:X}] [0x{pipe.arg2:X}]");
                pipe[0x3] = pipe[0x1] == pipe[0x2] ? 1ul : 0ul;
                if (x3 == 0x1 || x3 == 0x3)
                {
                    mem_types[pipe.result] = new i2_Type();
                }
                break;

            case 0xC6:     /* @neq */
                trace($"call :: neq [0x{pipe.arg1:X}] [0x{pipe.arg2:X}]");
                pipe[0x3] = pipe[0x1] != pipe[0x2] ? 1ul : 0ul;
                if (x3 == 0x1 || x3 == 0x3)
                {
                    mem_types[pipe.result] = new i2_Type();
                }
                break;

            case 0xC7:     /* @xor */
                trace($"call :: xor [0x{pipe.arg1:X}] [0x{pipe.arg2:X}]");
                if (!(mem_types[pipe.arg1] is i2_Type))
                {
                    bus.cpu.halt(0xA22, $"[0x{pipe.arg1:X}] is {mem_types[pipe.arg1].GetType().Name}");
                }
                if (!(mem_types[pipe.arg1] is i2_Type))
                {
                    bus.cpu.halt(0xA22, $"[0x{pipe.arg2:X}] is {mem_types[pipe.arg2].GetType().Name}");
                }

                pipe[0x3] = (pipe[0x1] == 0x1) ^ (pipe[0x2] == 0x1) ? 0x1ul : 0x0ul;
                if (x3 == 0x1 || x3 == 0x3)
                {
                    mem_types[pipe.result] = new i2_Type();
                }
                break;

            case 0xC8:     /* @or */
                trace($"call :: xor [0x{pipe.arg1:X}] [0x{pipe.arg2:X}]");
                if (!(mem_types[pipe.arg1] is i2_Type))
                {
                    bus.cpu.halt(0xA22, $"[0x{pipe.arg1:X}] is {mem_types[pipe.arg1].GetType().Name}");
                }
                if (!(mem_types[pipe.arg1] is i2_Type))
                {
                    bus.cpu.halt(0xA22, $"[0x{pipe.arg2:X}] is {mem_types[pipe.arg2].GetType().Name}");
                }

                pipe[0x3] = (pipe[0x1] == 0x1) | (pipe[0x2] == 0x1) ? 0x1ul : 0x0ul;
                if (x3 == 0x1 || x3 == 0x3)
                {
                    mem_types[pipe.result] = new i2_Type();
                }
                break;

            case 0xC9:     /* @and */
                trace($"call :: xor [0x{pipe.arg1:X}] [0x{pipe.arg2:X}]");
                if (!(mem_types[pipe.arg1] is i2_Type))
                {
                    bus.cpu.halt(0xA22, $"[0x{pipe.arg1:X}] is {mem_types[pipe.arg1].GetType().Name}");
                }
                if (!(mem_types[pipe.arg1] is i2_Type))
                {
                    bus.cpu.halt(0xA22, $"[0x{pipe.arg2:X}] is {mem_types[pipe.arg2].GetType().Name}");
                }

                pipe[0x3] = (pipe[0x1] == 0x1) & (pipe[0x2] == 0x1) ? 0x1ul : 0x0ul;
                if (x3 == 0x1 || x3 == 0x3)
                {
                    mem_types[pipe.result] = new i2_Type();
                }
                break;
                #region debug

            case 0xF when x2 == 0xF:     /* @mvx */
                string toString(ushort memAddr)
                {
                    var value = mem[memAddr];
                    var type  = mem_types[memAddr];

                    if (type is Unknown_Type)
                    {
                        return(ff ?
                               (u64f32 & value).ToString(CultureInfo.InvariantCulture) :
                               value.ToString(CultureInfo.InvariantCulture));
                    }
                    if (type is str_Type)
                    {
                        var p = StringLiteralMap.GetInternedString((int)value);
                        NativeString.Unwrap(p, out var result_str, true, true);
                        return(result_str ?? "<null>");
                    }
                    return($"<{type.GetType().Name.Replace("_Type", "").ToLowerInvariant()}>");
                }

                foreach (var uuu in toString(u1).Select(x => (int)x))
                {
                    bus.find(r1 & 0xFF).write(r2 & 0xFF, uuu);
                }
                break;

            case 0xC1 when x2 == 0x1:     /* @break :: now */
                trace($"[0x{iid:X}] @break :: now");
                bus.debugger.handleBreak(u16 & pc, this);
                mem[0x17] = 0x0;
                break;

            case 0xC1 when x2 == 0x2:     /* @break :: next */
                trace($"[0x{iid:X}] @break :: next");
                mem[0x17] = 0x1;
                break;

            case 0xC1 when x2 == 0x3:     /* @break :: after */
                trace($"[0x{iid:X}] @break :: after");
                mem[0x17] = 0x3;
                break;

                #endregion debug

                #region jumps

            case 0x8 when u2 == 0xF && x1 == 0x0:     /* @jump_t */
                trace($"jump_t 0x{r1:X}");
                warn("jump_t has obsolete");
                pc = mem[r1];
                break;

            case 0x8 when u2 == 0xF && x1 == 0x1:     /* @jump_e  0x8FCD0F10 */
                trace(mem[r2] >= mem[r3]
                        ? $"jump_e 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> apl"
                        : $"jump_e 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> skip");
                warn("jump_e has obsolete");
                if (mem[r2] >= mem[r3])
                {
                    pc = mem[r1];
                }
                break;

            case 0x8 when u2 == 0xF && x1 == 0x2:     /* @jump_g */
                trace(mem[r2] > mem[r3]
                        ? $"jump_g 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> apl"
                        : $"jump_g 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> skip");
                warn("jump_g has obsolete");
                if (mem[r2] > mem[r3])
                {
                    pc = mem[r1];
                }
                break;

            case 0x8 when u2 == 0xF && x1 == 0x3:     /* @jump_u */
                trace(mem[r2] < mem[r3]
                        ? $"jump_u 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> apl"
                        : $"jump_u 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> skip");
                warn("jump_u has obsolete");
                if (mem[r2] < mem[r3])
                {
                    pc = mem[r1];
                }
                break;

            case 0x8 when u2 == 0xF && x1 == 0x4:     /* @jump_y */
                trace(mem[r2] <= mem[r3]
                        ? $"jump_y 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> apl"
                        : $"jump_y 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> skip");
                warn("jump_y has obsolete");
                if (mem[r2] <= mem[r3])
                {
                    pc = mem[r1];
                }
                break;

            case 0x09 when x3 == 0x1:     /* @jump_p */
                pc = mem[(d8u)(u8 & r1, u8 & r2)];
                trace($"jump_p [0x{r1:X}{r2:X}] -> apl");
                break;

            case 0x09 when x3 == 0x2:     /* @jump_x */
                if (mem_types[(d8u)(u8 & r1, u8 & r2)] is u2_Type && mem[(d8u)(u8 & r1, u8 & r2)] == 0x1)
                {
                    pc = mem[(d8u)(u8 & r3, u8 & u1)];
                    trace($"jump_x [0x{r1:X}{r2:X}] -> true -> apl");
                    break;
                }
                trace($"jump_x [0x{r1:X}{r2:X}] -> false -> skip");
                break;
                #endregion jumps

                #region math

            case 0xCA:
                trace($"call :: add [0x{pipe.arg1:X}] [0x{pipe.arg2:X}] with 0x{x3:X} mode");
                if (ff)
                {
                    pipe[0x3] = f32u64 & (u64f32 & pipe[0x1]) + (u64f32 & pipe[0x2]);
                }
                else
                {
                    pipe[0x3] = pipe[0x1] + pipe[0x2];
                }
                break;

            case 0xCB:
                trace($"call :: sub [0x{pipe.arg1:X}] [0x{pipe.arg2:X}] with 0x{x3:X} mode");
                if (ff)
                {
                    pipe[0x3] = f32u64 & (u64f32 & pipe[0x1]) - (u64f32 & pipe[0x2]);
                }
                else
                {
                    pipe[0x3] = pipe[0x1] - pipe[0x2];
                }
                break;

            case 0xCC:
                trace($"call :: div [0x{pipe.arg1:X}] [0x{pipe.arg2:X}] with 0x{x3:X} mode");
                _ = (pipe[0x2], ff) switch
                {
                    (0x0, _) => (ulong)bus.cpu.halt(0xC),
                    (_, false) => pipe[0x3] = pipe[0x1] / pipe[0x2],
                    (_, true) => pipe[0x3]  = f32u64 & (u64f32 & pipe[0x1]) / (u64f32 & pipe[0x2])
                };
                break;

            case 0xCD:
                trace($"call :: mul [0x{pipe.arg1:X}] [0x{pipe.arg2:X}] with 0x{x3:X} mode");
                if (ff)
                {
                    pipe[0x3] = f32u64 & (u64f32 & pipe[0x1]) * (u64f32 & pipe[0x2]);
                }
                else
                {
                    pipe[0x3] = pipe[0x1] * pipe[0x2];
                }
                break;

            case 0xCE:
                trace($"call :: pow [0x{pipe.arg1:X}] [0x{pipe.arg2:X}] with 0x{x3:X} mode");
                if (ff)
                {
                    pipe[0x3] = f32u64 & Pow((u64f32 & pipe[0x1]), (u64f32 & pipe[0x2]));
                }
                else
                {
                    pipe[0x3] = (ulong)Pow(pipe[0x1], pipe[0x2]);
                }
                break;

            case 0xCF:
                trace($"call :: sqrt 0x{r2:X}");
                if (ff)
                {
                    pipe[0x3] = f32u64 & Sqrt((u64f32 & pipe[0x1]));
                }
                else
                {
                    pipe[0x3] = (ulong)Sqrt(pipe[0x1]);
                }
                break;

                #endregion math
            default:
                bus.cpu.halt(0xFC);
                Error($"call :: unknown opCode -> {iid:X2}");
                break;
            }
            /* @break :: after */
            if (mem[0x17] == 0x3)
            {
                mem[0x17] = 0x2;
            }
            if (mem[0x17] == 0x2)
            {
                bus.debugger.handleBreak(u16 & pc, this);
                mem[0x17] = 0x0;
            }
            /* @break :: end */
            IncrementClockStep();
        }
Exemplo n.º 3
0
        public void Eval()
        {
            using var watcher          = new StopwatchOperation("eval operation");
            MemoryManagement.FastWrite = fw;
            if (bf)
            {
                bus.debugger.handleBreak(u16 & pc, bus.cpu);
                mem[0x17] = 0x0;
            }

            switch (iid)
            {
            case 0x0:
                trace("call :: skip");
                break;

            case ushort opcode when opcode.In(0xD0..0xE8):
                /* need @float-flag */
                if (!ff)
                {
                    bus.cpu.halt(0xA9);
                }
                trace($"call :: [0xD0..0xE8]::0x{iid:X}");

                var result = iid switch {     // todo refactoring
                    0xD0 => f32u64& Abs(u64f32& mem[r1]),
                    0xD1 => f32u64& Acos(u64f32& mem[r1]),
                    0xD2 => f32u64& Atan(u64f32& mem[r1]),
                    0xD3 => f32u64& Acosh(u64f32& mem[r1]),
                    0xD4 => f32u64& Atanh(u64f32& mem[r1]),
                    0xD5 => f32u64& Asin(u64f32& mem[r1]),
                    0xD6 => f32u64& Asinh(u64f32& mem[r1]),
                    0xD7 => f32u64& Cbrt(u64f32& mem[r1]),
                    0xD8 => f32u64& Ceiling(u64f32& mem[r1]),
                    0xD9 => f32u64& Cos(u64f32& mem[r1]),
                    0xDA => f32u64& Cosh(u64f32& mem[r1]),

                    0xDB => f32u64& Floor(u64f32& mem[r1]),
                    0xDC => f32u64& Exp(u64f32& mem[r1]),
                    0xDD => f32u64& Log(u64f32& mem[r1]),
                    0xDE => f32u64& Log10(u64f32& mem[r1]),
                    0xDF => f32u64& Tan(u64f32& mem[r1]),
                    0xE0 => f32u64& Tanh(u64f32& mem[r1]),

                    0xE4 => f32u64& Atan2(u64f32& mem[r1], u64f32& mem[r2]),
                    0xE5 => f32u64& Min(u64f32& mem[r1], u64f32& mem[r2]),
                    0xE6 => f32u64& Max(u64f32& mem[r1], u64f32& mem[r2]),

                    0xE7 => f32u64& Sin(u64f32& mem[r1]),
                    0xE8 => f32u64& Sinh(u64f32& mem[r1]),

                    0xE1 => f32u64& Truncate(u64f32& mem[r1]),
                    0xE2 => f32u64& BitDecrement(u64f32& mem[r1]),
                    0xE3 => f32u64& BitIncrement(u64f32& mem[r1]),
                    _ => throw new CorruptedMemoryException($"")
                };
                /* @stack-forward-flag */
                if (sf)
                {
                    stack.push(result);
                }
                else
                {
                    mem[r1] = result;
                }
                break;

                #region halt
            case 0xF when r1 == 0xF && r2 == 0xF && r3 == 0xF && u1 == 0xF && u2 == 0xF && x1 == 0xF:
                bus.cpu.halt(0xF);
                break;

            case 0xD when r1 == 0xE && r2 == 0xA && r3 == 0xD:
                bus.cpu.halt(0x0);
                break;

            case 0xB when r1 == 0x0 && r2 == 0x0 && r3 == 0xB && u1 == 0x5:
                bus.cpu.halt(0x1);
                break;

                #endregion
            case 0x1 when x2 == 0x0:
                trace($"call :: ldi 0x{u1:X}, 0x{u2:X} -> 0x{r1:X}");
                _ = u2 switch
                {
                    0x0 => mem[r1] = u1,
                    _ => mem[r1]   = i64 | ((u2 << 4) | u1)
                };
                break;

            case 0x1 when x2 == 0xA:
                trace($"call :: ldx 0x{u1:X}, 0x{u2:X} -> 0x{r1:X}-0x{r2:X}");
                mem[((r1 << 4) | r2)] = i64 | ((u1 << 4) | u2);
                break;

            case 0x3:     /* @swap */
                trace($"call :: swap, 0x{r1:X}, 0x{r2:X}");
                mem[r1] ^= mem[r2];
                mem[r2]  = mem[r1] ^ mem[r2];
                mem[r1] ^= mem[r2];
                break;

            case 0xF when x2 == 0xE:     // 0xF9988E0
                trace($"call :: move, dev[0x{r1:X}] -> 0x{r2:X} -> 0x{u1:X}");
                bus.Find(r1 & 0xFF).write(r2 & 0xFF, i32 & mem[u1] & 0xFF);
                break;

            case 0xF when x2 == 0xC:     // 0xF00000C
                trace($"call :: move, dev[0x{r1:X}] -> 0x{r2:X} -> [0x{u1:X}-0x{u2:X}]");
                bus.Find(r1 & 0xFF).write(r2 & 0xFF, (r3 << 12 | u1 << 8 | u2 << 4 | x1) & 0xFFFFFFF);
                break;

            case 0xA4:
                trace($"call :: rfd dev[0x{r1:X}], 0x{r2:X}");
                stack.push(bus.Find(r1 & 0xFF).read(r2 & 0xFF));
                break;

            case 0x8 when u2 == 0xC:     /* @ref.t */
                trace($"call :: ref.t 0x{r1:X}");
                mem[r1] = pc;
                break;

            case 0xA0:
                trace($"call :: orb '{r1}' times");
                for (var i = pc + r1; pc != i;)
                {
                    stack.push(fetch());
                }
                break;

            case 0xA1:
                trace($"call :: pull -> 0x{r1:X}");
                mem[r1] = stack.pop();
                break;

            case 0xA5:     /* @sig */
                stack.push(mem[r2] = pc);
                var frag = default(ulong?);
                while (AcceptOpCode(frag) != 0xA6 /* @ret */)
                {
                    var pc_r = stack.pop();
                    frag = next(pc_r++);
                    if (frag is null)
                    {
                        bus.cpu.halt(0xA1);
                    }
                    stack.push(pc_r);
                }
                mem[r2 + 1] = stack.pop();
                break;

            case 0xB1:     /* @inc */
                trace($"call :: increment 0x{r1:X}++");
                unchecked { mem[r1]++; }
                break;

            case 0xB2:      /* @dec */
                trace($"call :: decrement 0x{r1:X}--");
                unchecked { mem[r1]--; }
                break;

            case 0xB3:     /* @dup */
                trace($"call :: dup 0x{(u2 << 4) | u1:X}");
                mem[(u2 << 4) | u1] = mem[(r1 << 4) | r2];
                break;

            case 0xB4:     /* @ckft */
                trace($"call :: ckft 0x{(r2 << 4) | r1:X}");
                if (ff && !float.IsFinite(f32i64 & mem[(r2 << 4) | r1]))
                {
                    bus.cpu.halt(0xA9);
                }
                break;

            case 0x36:     /* @call */
                d16u sign = (u8 & r3, u8 & u1, u8 & u2, u8 & x1);
                trace($"call :: call 0x{sign.Value:X}");
                var find = Module.Global.Find(sign, out var @extern);
                if (find != ExternStatus.Found)
                {
                    bus.cpu.halt(0xA16 + (int)find, $"0x{sign.Value:X}");
                    return;
                }
                trace($"call :: {@extern.Signature}");
                CallAndWrite(@extern);
                break;

            case 0x37:     /* @prune */
                evaluation.Prune();
                break;

            case 0x38:     /* @locals */
                d8u len = (u8 & r1, u8 & r2);
                evaluation.Alloc(len);
                var segs = new List <EvaluationSegment>(len);
                for (var i = 0; i != len; i++)
                {
                    segs.Add(EvaluationSegment.Construct(fetch(), fetch()));
                }
                var @params = new object[len];
                foreach (var s in segs)
                {
                    @params[s.Index] = ExternType.Find(s.Type);
                }
                locals.Pin(@params, out var @external);
                foreach (var(host, e_index) in @external.Select((x, i) => (x, i)))
                {
                    evaluation[e_index] = host;
                }
                break;

            case 0xB5:     /* @ixor */
            case 0xB6:     /* @ior */

                d8u first  = (u8 & r1, u8 & r2);
                d8u second = (u8 & u1, u8 & u2);
                // not support float-mode
                if (ff)
                {
                    bus.cpu.halt(0xA9);
                }
                if (iid == 0xB5)
                {
                    mem[first] ^= mem[second];
                }
                if (iid == 0xB6)
                {
                    mem[first] |= mem[second];
                }
                break;

            case 0x34:     /* @lpstr */
                d32u str_index = (u8 & r1, u8 & r2, u8 & r3, u8 & u1, u8 & u2, u8 & x1, u8 & x2, u8 & x3);
                if (!StringLiteralMap.Has(str_index))
                {
                    bus.cpu.halt(0xDE3, $"index {str_index.Value} not found in memory");
                }
                stack.push(str_index.Value);
                break;

            case 0x35:     /* @unlock */
                d8u unlock_cell = (u8 & r1, u8 & r2);
                mem[unlock_cell]       = stack.pop();
                mem_types[unlock_cell] = ExternType.Find(
                    (u8 & r3, u8 & u1),
                    (u8 & u2, u8 & x1),
                    (u8 & x2, u8 & x3)
                    );
                break;
                #region debug

            case 0xF when x2 == 0xF:     /* @mvx */
                string toString(ushort memAddr)
                {
                    var str   = default(string);
                    var value = mem[memAddr];
                    var type  = mem_types[memAddr];

                    if (type is Unknown_Type)
                    {
                        return(ff ?
                               (u64f32 & value).ToString(CultureInfo.InvariantCulture) :
                               value.ToString(CultureInfo.InvariantCulture));
                    }
                    if (type is str_Type)
                    {
                        var p = StringLiteralMap.GetInternedString((int)value);
                        NativeString.Unwrap(p, out var result_str, true, true);
                        return(result_str ?? "<null>");
                    }
                    return($"<{type.GetType().Name.Replace("_Type", "").ToLowerInvariant()}>");
                }

                foreach (var uuu in toString(u1).Select(x => (int)x))
                {
                    bus.Find(r1 & 0xFF).write(r2 & 0xFF, uuu);
                }
                break;

            case 0xC1 when x2 == 0x1:     /* @break :: now */
                trace($"[0x{iid:X}] @break :: now");
                bus.debugger.handleBreak(u16 & pc, bus.cpu);
                mem[0x17] = 0x0;
                break;

            case 0xC1 when x2 == 0x2:     /* @break :: next */
                trace($"[0x{iid:X}] @break :: next");
                mem[0x17] = 0x1;
                break;

            case 0xC1 when x2 == 0x3:     /* @break :: after */
                trace($"[0x{iid:X}] @break :: after");
                mem[0x17] = 0x3;
                break;

                #endregion
                #region jumps

            case 0x8 when u2 == 0xF && x1 == 0x0:     /* @jump_t */
                trace($"jump_t 0x{r1:X}");
                pc = mem[r1];
                break;

            case 0x8 when u2 == 0xF && x1 == 0x1:     /* @jump_e  0x8FCD0F10 */
                trace(mem[r2] >= mem[r3]
                        ? $"jump_e 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> apl"
                        : $"jump_e 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> skip");
                if (mem[r2] >= mem[r3])
                {
                    pc = mem[r1];
                }
                break;

            case 0x8 when u2 == 0xF && x1 == 0x2:     /* @jump_g */
                trace(mem[r2] > mem[r3]
                        ? $"jump_g 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> apl"
                        : $"jump_g 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> skip");
                if (mem[r2] > mem[r3])
                {
                    pc = mem[r1];
                }
                break;

            case 0x8 when u2 == 0xF && x1 == 0x3:     /* @jump_u */
                trace(mem[r2] < mem[r3]
                        ? $"jump_u 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> apl"
                        : $"jump_u 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> skip");
                if (mem[r2] < mem[r3])
                {
                    pc = mem[r1];
                }
                break;

            case 0x8 when u2 == 0xF && x1 == 0x4:     /* @jump_y */
                trace(mem[r2] <= mem[r3]
                        ? $"jump_y 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> apl"
                        : $"jump_y 0x{r1:X} -> 0x{r2:X} 0x{r3:X} -> skip");
                if (mem[r2] <= mem[r3])
                {
                    pc = mem[r1];
                }
                break;

            case 0x09:     /* @jump */
                pc = (ulong)((r1 << 4) | r2);
                break;

                #endregion
                #region math
            case 0xCA:
                trace($"call :: sum 0x{r2:X}, 0x{r3:X}");
                if (ff)
                {
                    mem[r1] = f32u64 & (u64f32 & mem[r2]) + (u64f32 & mem[r3]);
                }
                else
                {
                    mem[r1] = mem[r2] + mem[r3];
                }
                break;

            case 0xCB:
                trace($"call :: sub 0x{r2:X}, 0x{r3:X}");
                if (ff)
                {
                    mem[r1] = f32u64 & (u64f32 & mem[r2]) - (u64f32 & mem[r3]);
                }
                else
                {
                    mem[r1] = mem[r2] - mem[r3];
                }
                break;

            case 0xCC:
                trace($"call :: div 0x{r2:X}, 0x{r3:X}");
                _ = (mem[r3], ff) switch {
                    (0x0, _) => (ulong)bus.cpu.halt(0xC),
                    (_, false) => mem[r1] = mem[r2] / mem[r3],
                    (_, true) => mem[r1]  = f32u64 & (u64f32 & mem[r2]) / (u64f32 & mem[r3])
                };
                break;

            case 0xCD:
                trace($"call :: mul 0x{r2:X}, 0x{r3:X}");
                if (ff)
                {
                    mem[r1] = f32u64 & (u64f32 & mem[r2]) * (u64f32 & mem[r3]);
                }
                else
                {
                    mem[r1] = mem[r2] * mem[r3];
                }
                break;

            case 0xCE:
                trace($"call :: pow 0x{r2:X}, 0x{r3:X}");
                if (ff)
                {
                    mem[r1] = f32u64 & Pow(u64f32 & mem[r2], u64f32 & mem[r3]);
                }
                else
                {
                    mem[r1] = (uint)Math.Pow(mem[r2], mem[r3]);
                }
                break;

            case 0xCF:
                trace($"call :: sqrt 0x{r2:X}");
                if (ff)
                {
                    mem[r1] = f32u64 & Sqrt(u64f32 & mem[r2]);
                }
                else
                {
                    mem[r1] = (uint)Math.Sqrt(mem[r2]);
                }
                break;

                #endregion
            default:
                bus.cpu.halt(0xFC);
                Error($"call :: unknown opCode -> {iid:X2}");
                break;
            }
            /* @break :: after */
            if (mem[0x17] == 0x3)
            {
                mem[0x17] = 0x2;
            }
            if (mem[0x17] == 0x2)
            {
                bus.debugger.handleBreak(u16 & pc, bus.cpu);
                mem[0x17] = 0x0;
            }
            /* @break :: end */
        }