package vm import "fmt" type State struct { running bool err error program *Program functionIndex int64 instructionIndex int64 comparisonResult bool globalMemory *Memory stack []*stackFrame } func NewState(prog *Program) (*State, error) { return &State{ program: prog, globalMemory: NewMemory(16), }, nil } func (state *State) Execute() error { state.setHandlers() state.call(0) state.running = true for state.running { state.processInstruction() } return state.err } func (state *State) GlobalMemory() *Memory { return state.globalMemory } func (state *State) stackFrame() *stackFrame { return state.stack[len(state.stack)-1] } func (state *State) function() *Function { return state.program.Functions[state.functionIndex] } func (state *State) setError(err error) { state.err = err state.running = false } func (state *State) setHandlers() { for _, fnc := range state.program.Functions { for _, instr := range fnc.Instructions { handler, found := opHandlers[instr.OpCode] if !found { state.setError(fmt.Errorf("Invalid OpCode: 0x%08x", instr.OpCode)) return } instr.opHandler = handler } } } func (state *State) processInstruction() { fnc := state.function() instr := fnc.Instructions[state.instructionIndex] state.instructionIndex += 1 instr.opHandler(state, instr) if state.functionIndex >= int64(len(state.program.Functions)) { state.ret() } if state.instructionIndex >= int64(len(fnc.Instructions)) { state.ret() } } func (state *State) readUnsigned(op *Operand) uint64 { switch op.Type { case Literal: return op.Value case FunctionMemoryIndex: value, err := state.stackFrame().functionMemory.ReadUnsigned(op.Value) if err != nil { state.setError(err) } return value case GlobalMemoryIndex: value, err := state.globalMemory.ReadUnsigned(op.Value) if err != nil { state.setError(err) } return value default: state.setError(fmt.Errorf("Unknown operand type: 0x%02x", op.Type)) return 0 } } func (state *State) readSigned(op *Operand) int64 { return int64(state.readUnsigned(op)) } func (state *State) writeUnsigned(op *Operand, value uint64) { switch op.Type { case Literal: state.setError(fmt.Errorf("Write to literal operand")) case FunctionMemoryIndex: err := state.stackFrame().functionMemory.WriteUnsigned(op.Value, value) if err != nil { state.setError(err) } case GlobalMemoryIndex: err := state.globalMemory.WriteUnsigned(op.Value, value) if err != nil { state.setError(err) } default: state.setError(fmt.Errorf("Unknown operand type: 0x%02x", op.Type)) } } func (state *State) writeSigned(op *Operand, value int64) { state.writeUnsigned(op, uint64(value)) } func (state *State) call(functionOffset int64) { stackFrame := newStackFrame(state) state.stack = append(state.stack, stackFrame) state.functionIndex += functionOffset state.instructionIndex = 0 } func (state *State) ret() { state.functionIndex = state.stackFrame().previousFunctionIndex state.instructionIndex = state.stackFrame().previousInstructionIndex state.stack = state.stack[:len(state.stack)-1] if len(state.stack) == 0 { state.running = false } } func (state *State) jump(instructionOffset int64) { // -1 accounts for the +1 processInstruction() state.instructionIndex += instructionOffset - 1 }