116 lines
6.6 KiB
C#
116 lines
6.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using STCompiler.Common;
|
|
using System.Collections.Generic;
|
|
|
|
class Program {
|
|
static int Main(string[] args) {
|
|
if (args.Length < 1) {
|
|
Console.WriteLine("Usage: StSim <file.stbc>");
|
|
return 1;
|
|
}
|
|
var path = args[0];
|
|
if (!File.Exists(path)) { Console.WriteLine("File not found: " + path); return 2; }
|
|
var data = File.ReadAllBytes(path);
|
|
try { Simulate(data); } catch(Exception ex) { Console.WriteLine("Error: " + ex.Message); return 3; }
|
|
return 0;
|
|
}
|
|
|
|
static void Simulate(byte[] data) {
|
|
using var ms = new MemoryStream(data);
|
|
using var r = new BinaryReader(ms);
|
|
|
|
string magic = System.Text.Encoding.ASCII.GetString(r.ReadBytes(4));
|
|
if (magic != Bytecode.Magic) throw new Exception("Invalid magic");
|
|
ushort ver = r.ReadUInt16();
|
|
Console.WriteLine($"Version: {ver}");
|
|
bool oldFormat = ver < Bytecode.Version;
|
|
|
|
ushort nConsts = r.ReadUInt16();
|
|
Console.WriteLine($"Consts: {nConsts}");
|
|
var consts = new List<object>();
|
|
if (oldFormat) {
|
|
// old format: constants stored as 4-byte ints
|
|
for (int i = 0; i < nConsts; i++) { int v = r.ReadInt32(); consts.Add(v); Console.WriteLine($" [{i}] = {v}"); }
|
|
} else {
|
|
for (int i = 0; i < nConsts; i++) {
|
|
byte t = r.ReadByte();
|
|
switch(t) {
|
|
case 1: { long v = r.ReadInt64(); consts.Add(v); Console.WriteLine($" [{i}] (long) = {v}"); break; }
|
|
case 2: { double v = r.ReadDouble(); consts.Add(v); Console.WriteLine($" [{i}] (double) = {v}"); break; }
|
|
case 3: { float v = r.ReadSingle(); consts.Add(v); Console.WriteLine($" [{i}] (float) = {v}"); break; }
|
|
case 4: { int v = r.ReadInt32(); consts.Add(v); Console.WriteLine($" [{i}] (int) = {v}"); break; }
|
|
default: { Console.WriteLine($" [{i}] Unknown const type {t}"); break; }
|
|
}
|
|
}
|
|
}
|
|
|
|
ushort nVars = r.ReadUInt16();
|
|
Console.WriteLine($"Vars: {nVars}");
|
|
var varTypes = new byte[nVars];
|
|
for (int i = 0; i < nVars; i++) { varTypes[i] = r.ReadByte(); Console.WriteLine($" Var[{i}] type = {varTypes[i]}"); }
|
|
|
|
ushort codeLen = r.ReadUInt16();
|
|
Console.WriteLine($"CodeLen: {codeLen} bytes");
|
|
var code = r.ReadBytes(codeLen);
|
|
|
|
var stack = new Stack<object>();
|
|
var vars = new object[nVars];
|
|
int ip = 0;
|
|
Console.WriteLine("\n--- Disassembly / Simulation ---");
|
|
while (ip < code.Length) {
|
|
int addr = ip;
|
|
byte op = code[ip++];
|
|
Console.Write($"{addr:0000}: 0x{op:X2} {Bytecode.OpName(op)} ");
|
|
switch (op) {
|
|
case Bytecode.OpCodes.NOP: Console.WriteLine("NOP"); break;
|
|
case Bytecode.OpCodes.PUSH_CONST: {
|
|
ushort ci = (ushort)(code[ip++] | (code[ip++] << 8));
|
|
if (oldFormat) { byte typeMarker = code[ip++]; /* skip legacy type byte */ }
|
|
Console.WriteLine($"PUSH_CONST {ci} ({consts[ci]})");
|
|
stack.Push(consts[ci]);
|
|
break; }
|
|
case Bytecode.OpCodes.PUSH_REAL_CONST: {
|
|
ushort ci = (ushort)(code[ip++] | (code[ip++] << 8));
|
|
if (oldFormat) { byte typeMarker = code[ip++]; /* skip legacy type byte */ }
|
|
Console.WriteLine($"PUSH_REAL_CONST {ci} ({consts[ci]})");
|
|
stack.Push(consts[ci]);
|
|
break; }
|
|
case Bytecode.OpCodes.LOAD_VAR: { ushort vi = (ushort)(code[ip++] | (code[ip++] << 8)); Console.WriteLine($"LOAD_VAR {vi}"); stack.Push(vars[vi]); break; }
|
|
case Bytecode.OpCodes.STORE_VAR: { ushort vi = (ushort)(code[ip++] | (code[ip++] << 8)); Console.WriteLine($"STORE_VAR {vi}"); vars[vi] = stack.Pop(); break; }
|
|
case Bytecode.OpCodes.JZ: { ushort target = (ushort)(code[ip++] | (code[ip++] << 8)); Console.WriteLine($"JZ addr={target}"); var cond = stack.Pop(); bool isFalse = cond is int ci ? ci == 0 : cond is long cl ? cl == 0L : cond is double cd ? cd == 0.0 : cond == null; if (isFalse) ip = target; break; }
|
|
case Bytecode.OpCodes.JMP: { ushort target = (ushort)(code[ip++] | (code[ip++] << 8)); Console.WriteLine($"JMP addr={target}"); ip = target; break; }
|
|
case Bytecode.OpCodes.HALT: Console.WriteLine("HALT"); ip = code.Length; break;
|
|
default: Console.WriteLine();
|
|
// fallback handlers
|
|
if (Bytecode.OpName(op).StartsWith("ADD_") || Bytecode.OpName(op).StartsWith("SUB_") || Bytecode.OpName(op).StartsWith("MUL_") || Bytecode.OpName(op).StartsWith("DIV_")) {
|
|
dynamic b = stack.Pop(); dynamic a = stack.Pop(); if (Bytecode.OpName(op).StartsWith("ADD_")) stack.Push(a + b); else if (Bytecode.OpName(op).StartsWith("SUB_")) stack.Push(a - b); else if (Bytecode.OpName(op).StartsWith("MUL_")) stack.Push(a * b); else stack.Push(a / b);
|
|
break;
|
|
}
|
|
if (Bytecode.OpName(op).StartsWith("LT_") || Bytecode.OpName(op).StartsWith("GT_") || Bytecode.OpName(op).StartsWith("LE_") || Bytecode.OpName(op).StartsWith("GE_") || Bytecode.OpName(op).StartsWith("EQ_") || Bytecode.OpName(op).StartsWith("NEQ_")) {
|
|
dynamic rVal = stack.Pop(); dynamic lVal = stack.Pop();
|
|
bool res = Bytecode.OpName(op).StartsWith("LT_") ? (lVal < rVal) :
|
|
Bytecode.OpName(op).StartsWith("GT_") ? (lVal > rVal) :
|
|
Bytecode.OpName(op).StartsWith("LE_") ? (lVal <= rVal) :
|
|
Bytecode.OpName(op).StartsWith("GE_") ? (lVal >= rVal) :
|
|
Bytecode.OpName(op).StartsWith("EQ_") ? (lVal == rVal) :
|
|
(lVal != rVal);
|
|
stack.Push(res ? 1 : 0);
|
|
break;
|
|
}
|
|
throw new Exception($"Unknown opcode 0x{op:X2}");
|
|
}
|
|
}
|
|
|
|
Console.WriteLine("Execution finished\n");
|
|
// Detailed variable summary
|
|
Console.WriteLine("=== Variable summary ===");
|
|
Console.WriteLine("Index\tType\t\tValue");
|
|
for (int i = 0; i < vars.Length; i++) {
|
|
string typeName = (i < varTypes.Length) ? ((VarType)varTypes[i]).ToString() : "Unknown";
|
|
var value = vars[i] ?? "null";
|
|
Console.WriteLine($"{i}\t{typeName.PadRight(8)}\t{value}");
|
|
}
|
|
}
|
|
}
|