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 "); 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(); if (ver != Bytecode.Version) throw new Exception($"Unsupported version {ver}"); ushort nConsts = r.ReadUInt16(); var consts = new List(); for (int i = 0; i < nConsts; i++) { byte t = r.ReadByte(); switch(t) { case 1: consts.Add(r.ReadInt64()); break; case 2: consts.Add(r.ReadDouble()); break; case 3: consts.Add(r.ReadSingle()); break; case 4: consts.Add(r.ReadInt32()); break; default: throw new Exception($"Unknown const type {t}"); } } ushort nVars = r.ReadUInt16(); var varTypes = new VarType[nVars]; for (int i = 0; i < nVars; i++) varTypes[i] = (VarType)r.ReadByte(); ushort codeLen = r.ReadUInt16(); var code = r.ReadBytes(codeLen); var stack = new Stack(); var vars = new object[nVars]; int ip = 0; while (ip < code.Length) { byte op = code[ip++]; switch(op) { case Bytecode.OpCodes.NOP: break; case Bytecode.OpCodes.PUSH_CONST: { ushort ci = (ushort)(code[ip++] | (code[ip++] << 8)); stack.Push(consts[ci]); break; } case Bytecode.OpCodes.PUSH_REAL_CONST: { ushort ci = (ushort)(code[ip++] | (code[ip++] << 8)); stack.Push(consts[ci]); break; } case Bytecode.OpCodes.LOAD_VAR: { ushort vi = (ushort)(code[ip++] | (code[ip++] << 8)); stack.Push(vars[vi]); break; } case Bytecode.OpCodes.STORE_VAR: { ushort vi = (ushort)(code[ip++] | (code[ip++] << 8)); vars[vi] = stack.Pop(); break; } case Bytecode.OpCodes.JZ: { ushort target = (ushort)(code[ip++] | (code[ip++] << 8)); 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)); ip = target; break; } case Bytecode.OpCodes.HALT: Console.WriteLine("HALT"); return; default: // Simple arithmetic handlers for some opcodes if (Bytecode.OpName(op).StartsWith("ADD_")) { dynamic b = stack.Pop(); dynamic a = stack.Pop(); stack.Push(a + b); break; } if (Bytecode.OpName(op).StartsWith("SUB_")) { dynamic b = stack.Pop(); dynamic a = stack.Pop(); stack.Push(a - b); break; } if (Bytecode.OpName(op).StartsWith("MUL_")) { dynamic b = stack.Pop(); dynamic a = stack.Pop(); stack.Push(a * b); break; } if (Bytecode.OpName(op).StartsWith("DIV_")) { dynamic b = stack.Pop(); dynamic a = stack.Pop(); 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_")) { // comparisons: pop r, pop l, push int 0/1 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"); for (int i = 0; i < vars.Length; i++) Console.WriteLine($"Var[{i}] = {vars[i]}"); } }