|
|
@ -12,46 +12,28 @@ import ( |
|
|
|
// Op codes
|
|
|
|
// Op codes
|
|
|
|
const ( |
|
|
|
const ( |
|
|
|
oSTOP int = 0x00 |
|
|
|
oSTOP int = 0x00 |
|
|
|
|
|
|
|
oADD int = 0x01 |
|
|
|
|
|
|
|
oMUL int = 0x02 |
|
|
|
|
|
|
|
oSUB int = 0x03 |
|
|
|
|
|
|
|
oDIV int = 0x04 |
|
|
|
|
|
|
|
oSDIV int = 0x05 |
|
|
|
|
|
|
|
oMOD int = 0x06 |
|
|
|
|
|
|
|
oSMOD int = 0x07 |
|
|
|
|
|
|
|
oEXP int = 0x08 |
|
|
|
|
|
|
|
oNEG int = 0x09 |
|
|
|
|
|
|
|
oLT int = 0x0a |
|
|
|
|
|
|
|
oLE int = 0x0b |
|
|
|
|
|
|
|
oGT int = 0x0c |
|
|
|
|
|
|
|
oGE int = 0x0d |
|
|
|
|
|
|
|
oEQ int = 0x0e |
|
|
|
|
|
|
|
oNOT int = 0x0f |
|
|
|
|
|
|
|
oMYADDRESS int = 0x10 |
|
|
|
|
|
|
|
oTXSENDER int = 0x11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
oPUSH int = 0x30 |
|
|
|
oPUSH int = 0x30 |
|
|
|
oPOP int = 0x31 |
|
|
|
oPOP int = 0x31 |
|
|
|
oLOAD int = 0x36 |
|
|
|
oLOAD int = 0x36 |
|
|
|
/* |
|
|
|
|
|
|
|
oADD int = 0x10 |
|
|
|
|
|
|
|
oSUB int = 0x11 |
|
|
|
|
|
|
|
oMUL int = 0x12 |
|
|
|
|
|
|
|
oDIV int = 0x13 |
|
|
|
|
|
|
|
oSDIV int = 0x14 |
|
|
|
|
|
|
|
oMOD int = 0x15 |
|
|
|
|
|
|
|
oSMOD int = 0x16 |
|
|
|
|
|
|
|
oEXP int = 0x17 |
|
|
|
|
|
|
|
oNEG int = 0x18 |
|
|
|
|
|
|
|
oLT int = 0x20 |
|
|
|
|
|
|
|
oLE int = 0x21 |
|
|
|
|
|
|
|
oGT int = 0x22 |
|
|
|
|
|
|
|
oGE int = 0x23 |
|
|
|
|
|
|
|
oEQ int = 0x24 |
|
|
|
|
|
|
|
oNOT int = 0x25 |
|
|
|
|
|
|
|
oSHA256 int = 0x30 |
|
|
|
|
|
|
|
oRIPEMD160 int = 0x31 |
|
|
|
|
|
|
|
oECMUL int = 0x32 |
|
|
|
|
|
|
|
oECADD int = 0x33 |
|
|
|
|
|
|
|
oSIGN int = 0x34 |
|
|
|
|
|
|
|
oRECOVER int = 0x35 |
|
|
|
|
|
|
|
oCOPY int = 0x40 |
|
|
|
|
|
|
|
oST int = 0x41 |
|
|
|
|
|
|
|
oLD int = 0x42 |
|
|
|
|
|
|
|
oSET int = 0x43 |
|
|
|
|
|
|
|
oJMP int = 0x50 |
|
|
|
|
|
|
|
oJMPI int = 0x51 |
|
|
|
|
|
|
|
oIND int = 0x52 |
|
|
|
|
|
|
|
oEXTRO int = 0x60 |
|
|
|
|
|
|
|
oBALANCE int = 0x61 |
|
|
|
|
|
|
|
oMKTX int = 0x70 |
|
|
|
|
|
|
|
oDATA int = 0x80 |
|
|
|
|
|
|
|
oDATAN int = 0x81 |
|
|
|
|
|
|
|
oMYADDRESS int = 0x90 |
|
|
|
|
|
|
|
oSUICIDE int = 0xff |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
type OpType int |
|
|
|
type OpType int |
|
|
@ -79,9 +61,21 @@ func (st *Stack) Pop() string { |
|
|
|
return str |
|
|
|
return str |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (st *Stack) Popn() (*big.Int, *big.Int) { |
|
|
|
|
|
|
|
s := len(st.data) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
strs := st.data[s-2:] |
|
|
|
|
|
|
|
st.data = st.data[:s-2] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Big(strs[0]), Big(strs[1]) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (st *Stack) Push(d string) { |
|
|
|
func (st *Stack) Push(d string) { |
|
|
|
st.data = append(st.data, d) |
|
|
|
st.data = append(st.data, d) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (st *Stack) Print() { |
|
|
|
|
|
|
|
fmt.Println(st.data) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type Vm struct { |
|
|
|
type Vm struct { |
|
|
|
// Stack
|
|
|
|
// Stack
|
|
|
@ -96,7 +90,7 @@ func NewVm() *Vm { |
|
|
|
|
|
|
|
|
|
|
|
func (vm *Vm) ProcContract(tx *Transaction, block *Block, cb TxCallback) { |
|
|
|
func (vm *Vm) ProcContract(tx *Transaction, block *Block, cb TxCallback) { |
|
|
|
// Instruction pointer
|
|
|
|
// Instruction pointer
|
|
|
|
iptr := 0 |
|
|
|
pc := 0 |
|
|
|
|
|
|
|
|
|
|
|
contract := block.GetContract(tx.Hash()) |
|
|
|
contract := block.GetContract(tx.Hash()) |
|
|
|
if contract == nil { |
|
|
|
if contract == nil { |
|
|
@ -104,164 +98,144 @@ func (vm *Vm) ProcContract(tx *Transaction, block *Block, cb TxCallback) { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Pow256 := BigPow(2, 256) |
|
|
|
|
|
|
|
|
|
|
|
fmt.Printf("# op arg\n") |
|
|
|
fmt.Printf("# op arg\n") |
|
|
|
out: |
|
|
|
out: |
|
|
|
for { |
|
|
|
for { |
|
|
|
// The base big int for all calculations. Use this for any results.
|
|
|
|
// The base big int for all calculations. Use this for any results.
|
|
|
|
base := new(big.Int) |
|
|
|
base := new(big.Int) |
|
|
|
base.SetString("0",0) // so it doesn't whine about it
|
|
|
|
|
|
|
|
// XXX Should Instr return big int slice instead of string slice?
|
|
|
|
// XXX Should Instr return big int slice instead of string slice?
|
|
|
|
// Get the next instruction from the contract
|
|
|
|
// Get the next instruction from the contract
|
|
|
|
op, args, _ := Instr(contract.state.Get(string(Encode(uint32(iptr))))) |
|
|
|
//op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
|
|
|
|
|
|
|
|
op, _, _ := Instr(contract.state.Get(string(NumberToBytes(uint64(pc), 32)))) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !cb(0) { break } |
|
|
|
|
|
|
|
|
|
|
|
if Debug { |
|
|
|
if Debug { |
|
|
|
fmt.Printf("%-3d %-4d %v\n", iptr, op, args) |
|
|
|
fmt.Printf("%-3d %-4d\n", pc, op) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
switch op { |
|
|
|
switch op { |
|
|
|
|
|
|
|
case oADD: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// (x + y) % 2 ** 256
|
|
|
|
|
|
|
|
base.Add(x, y) |
|
|
|
|
|
|
|
base.Mod(base, Pow256) |
|
|
|
|
|
|
|
// Pop result back on the stack
|
|
|
|
|
|
|
|
vm.stack.Push(base.String()) |
|
|
|
|
|
|
|
case oSUB: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// (x - y) % 2 ** 256
|
|
|
|
|
|
|
|
base.Sub(x, y) |
|
|
|
|
|
|
|
base.Mod(base, Pow256) |
|
|
|
|
|
|
|
// Pop result back on the stack
|
|
|
|
|
|
|
|
vm.stack.Push(base.String()) |
|
|
|
|
|
|
|
case oMUL: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// (x * y) % 2 ** 256
|
|
|
|
|
|
|
|
base.Mul(x, y) |
|
|
|
|
|
|
|
base.Mod(base, Pow256) |
|
|
|
|
|
|
|
// Pop result back on the stack
|
|
|
|
|
|
|
|
vm.stack.Push(base.String()) |
|
|
|
|
|
|
|
case oDIV: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// floor(x / y)
|
|
|
|
|
|
|
|
base.Div(x, y) |
|
|
|
|
|
|
|
// Pop result back on the stack
|
|
|
|
|
|
|
|
vm.stack.Push(base.String()) |
|
|
|
|
|
|
|
case oSDIV: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// n > 2**255
|
|
|
|
|
|
|
|
if x.Cmp(Pow256) > 0 { x.Sub(Pow256, x) } |
|
|
|
|
|
|
|
if y.Cmp(Pow256) > 0 { y.Sub(Pow256, y) } |
|
|
|
|
|
|
|
z := new(big.Int) |
|
|
|
|
|
|
|
z.Div(x, y) |
|
|
|
|
|
|
|
if z.Cmp(Pow256) > 0 { z.Sub(Pow256, z) } |
|
|
|
|
|
|
|
// Push result on to the stack
|
|
|
|
|
|
|
|
vm.stack.Push(z.String()) |
|
|
|
|
|
|
|
case oMOD: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
base.Mod(x, y) |
|
|
|
|
|
|
|
vm.stack.Push(base.String()) |
|
|
|
|
|
|
|
case oSMOD: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// n > 2**255
|
|
|
|
|
|
|
|
if x.Cmp(Pow256) > 0 { x.Sub(Pow256, x) } |
|
|
|
|
|
|
|
if y.Cmp(Pow256) > 0 { y.Sub(Pow256, y) } |
|
|
|
|
|
|
|
z := new(big.Int) |
|
|
|
|
|
|
|
z.Mod(x, y) |
|
|
|
|
|
|
|
if z.Cmp(Pow256) > 0 { z.Sub(Pow256, z) } |
|
|
|
|
|
|
|
// Push result on to the stack
|
|
|
|
|
|
|
|
vm.stack.Push(z.String()) |
|
|
|
|
|
|
|
case oEXP: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
base.Exp(x, y, Pow256) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vm.stack.Push(base.String()) |
|
|
|
|
|
|
|
case oNEG: |
|
|
|
|
|
|
|
base.Sub(Pow256, Big(vm.stack.Pop())) |
|
|
|
|
|
|
|
vm.stack.Push(base.String()) |
|
|
|
|
|
|
|
case oLT: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// x < y
|
|
|
|
|
|
|
|
if x.Cmp(y) < 0 { |
|
|
|
|
|
|
|
vm.stack.Push("1") |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
vm.stack.Push("0") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case oLE: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// x <= y
|
|
|
|
|
|
|
|
if x.Cmp(y) < 1 { |
|
|
|
|
|
|
|
vm.stack.Push("1") |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
vm.stack.Push("0") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case oGT: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// x > y
|
|
|
|
|
|
|
|
if x.Cmp(y) > 0 { |
|
|
|
|
|
|
|
vm.stack.Push("1") |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
vm.stack.Push("0") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case oGE: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// x >= y
|
|
|
|
|
|
|
|
if x.Cmp(y) > -1 { |
|
|
|
|
|
|
|
vm.stack.Push("1") |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
vm.stack.Push("0") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case oNOT: |
|
|
|
|
|
|
|
x, y := vm.stack.Popn() |
|
|
|
|
|
|
|
// x != y
|
|
|
|
|
|
|
|
if x.Cmp(y) != 0 { |
|
|
|
|
|
|
|
vm.stack.Push("1") |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
vm.stack.Push("0") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case oMYADDRESS: |
|
|
|
|
|
|
|
vm.stack.Push(string(tx.Hash())) |
|
|
|
|
|
|
|
case oTXSENDER: |
|
|
|
|
|
|
|
vm.stack.Push(tx.sender) |
|
|
|
case oPUSH: |
|
|
|
case oPUSH: |
|
|
|
// Get the next entry and pushes the value on the stack
|
|
|
|
// Get the next entry and pushes the value on the stack
|
|
|
|
iptr++ |
|
|
|
pc++ |
|
|
|
vm.stack.Push(contract.state.Get(string(Encode(uint32(iptr))))) |
|
|
|
vm.stack.Push(contract.state.Get(string(NumberToBytes(uint64(pc), 32)))) |
|
|
|
case oPOP: |
|
|
|
case oPOP: |
|
|
|
// Pop current value of the stack
|
|
|
|
// Pop current value of the stack
|
|
|
|
vm.stack.Pop() |
|
|
|
vm.stack.Pop() |
|
|
|
case oLOAD: |
|
|
|
case oLOAD: |
|
|
|
// Load instruction X on the stack
|
|
|
|
// Load instruction X on the stack
|
|
|
|
i, _ := strconv.Atoi(vm.stack.Pop()) |
|
|
|
i, _ := strconv.Atoi(vm.stack.Pop()) |
|
|
|
vm.stack.Push(contract.state.Get(string(Encode(uint32(i))))) |
|
|
|
vm.stack.Push(contract.state.Get(string(NumberToBytes(uint64(i), 32)))) |
|
|
|
case oSTOP: |
|
|
|
case oSTOP: |
|
|
|
break out |
|
|
|
break out |
|
|
|
} |
|
|
|
} |
|
|
|
iptr++ |
|
|
|
pc++ |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
vm.stack.Print() |
|
|
|
type Vm struct { |
|
|
|
|
|
|
|
// Memory stack
|
|
|
|
|
|
|
|
stack map[string]string |
|
|
|
|
|
|
|
memory map[string]map[string]string |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func NewVm() *Vm { |
|
|
|
|
|
|
|
//stackSize := uint(256)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return &Vm{ |
|
|
|
|
|
|
|
stack: make(map[string]string), |
|
|
|
|
|
|
|
memory: make(map[string]map[string]string), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (vm *Vm) RunTransaction(tx *Transaction, cb TxCallback) { |
|
|
|
|
|
|
|
if Debug { |
|
|
|
|
|
|
|
fmt.Printf(` |
|
|
|
|
|
|
|
# processing Tx (%v) |
|
|
|
|
|
|
|
# fee = %f, ops = %d, sender = %s, value = %d |
|
|
|
|
|
|
|
`, tx.addr, float32(tx.fee) / 1e8, len(tx.data), tx.sender, tx.value) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vm.stack = make(map[string]string) |
|
|
|
|
|
|
|
vm.stack["0"] = tx.sender |
|
|
|
|
|
|
|
vm.stack["1"] = "100" //int(tx.value)
|
|
|
|
|
|
|
|
vm.stack["1"] = "1000" //int(tx.fee)
|
|
|
|
|
|
|
|
// Stack pointer
|
|
|
|
|
|
|
|
stPtr := 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//vm.memory[tx.addr] = make([]int, 256)
|
|
|
|
|
|
|
|
vm.memory[tx.addr] = make(map[string]string) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Define instruction 'accessors' for the instruction, which makes it more readable
|
|
|
|
|
|
|
|
// also called register values, shorthanded as Rx/y/z. Memory address are shorthanded as Mx/y/z.
|
|
|
|
|
|
|
|
// Instructions are shorthanded as Ix/y/z
|
|
|
|
|
|
|
|
x := 0; y := 1; z := 2; //a := 3; b := 4; c := 5
|
|
|
|
|
|
|
|
out: |
|
|
|
|
|
|
|
for stPtr < len(tx.data) { |
|
|
|
|
|
|
|
// The base big int for all calculations. Use this for any results.
|
|
|
|
|
|
|
|
base := new(big.Int) |
|
|
|
|
|
|
|
// XXX Should Instr return big int slice instead of string slice?
|
|
|
|
|
|
|
|
op, args, _ := Instr(tx.data[stPtr]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if Debug { |
|
|
|
|
|
|
|
fmt.Printf("%-3d %d %v\n", stPtr, op, args) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
opType := OpType(tNorm) |
|
|
|
|
|
|
|
// Determine the op type (used for calculating fees by the block manager)
|
|
|
|
|
|
|
|
switch op { |
|
|
|
|
|
|
|
case oEXTRO, oBALANCE: |
|
|
|
|
|
|
|
opType = tExtro |
|
|
|
|
|
|
|
case oSHA256, oRIPEMD160, oECMUL, oECADD: // TODO add rest
|
|
|
|
|
|
|
|
opType = tCrypto |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If the callback yielded a negative result abort execution
|
|
|
|
|
|
|
|
if !cb(opType) { break out } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nptr := stPtr |
|
|
|
|
|
|
|
switch op { |
|
|
|
|
|
|
|
case oSTOP: |
|
|
|
|
|
|
|
fmt.Println("exiting (oSTOP), idx =", nptr) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
break out |
|
|
|
|
|
|
|
case oADD: |
|
|
|
|
|
|
|
// (Rx + Ry) % 2 ** 256
|
|
|
|
|
|
|
|
base.Add(Big(vm.stack[args[ x ]]), Big(vm.stack[args[ y ]])) |
|
|
|
|
|
|
|
base.Mod(base, big.NewInt(int64(math.Pow(2, 256)))) |
|
|
|
|
|
|
|
// Set the result to Rz
|
|
|
|
|
|
|
|
vm.stack[args[ z ]] = base.String() |
|
|
|
|
|
|
|
case oSUB: |
|
|
|
|
|
|
|
// (Rx - Ry) % 2 ** 256
|
|
|
|
|
|
|
|
base.Sub(Big(vm.stack[args[ x ]]), Big(vm.stack[args[ y ]])) |
|
|
|
|
|
|
|
base.Mod(base, big.NewInt(int64(math.Pow(2, 256)))) |
|
|
|
|
|
|
|
// Set the result to Rz
|
|
|
|
|
|
|
|
vm.stack[args[ z ]] = base.String() |
|
|
|
|
|
|
|
case oMUL: |
|
|
|
|
|
|
|
// (Rx * Ry) % 2 ** 256
|
|
|
|
|
|
|
|
base.Mul(Big(vm.stack[args[ x ]]), Big(vm.stack[args[ y ]])) |
|
|
|
|
|
|
|
base.Mod(base, big.NewInt(int64(math.Pow(2, 256)))) |
|
|
|
|
|
|
|
// Set the result to Rz
|
|
|
|
|
|
|
|
vm.stack[args[ z ]] = base.String() |
|
|
|
|
|
|
|
case oDIV: |
|
|
|
|
|
|
|
// floor(Rx / Ry)
|
|
|
|
|
|
|
|
base.Div(Big(vm.stack[args[ x ]]), Big(vm.stack[args[ y ]])) |
|
|
|
|
|
|
|
// Set the result to Rz
|
|
|
|
|
|
|
|
vm.stack[args[ z ]] = base.String() |
|
|
|
|
|
|
|
case oSET: |
|
|
|
|
|
|
|
// Set the (numeric) value at Iy to Rx
|
|
|
|
|
|
|
|
vm.stack[args[ x ]] = args[ y ] |
|
|
|
|
|
|
|
case oLD: |
|
|
|
|
|
|
|
// Load the value at Mx to Ry
|
|
|
|
|
|
|
|
vm.stack[args[ y ]] = vm.memory[tx.addr][vm.stack[args[ x ]]] |
|
|
|
|
|
|
|
case oLT: |
|
|
|
|
|
|
|
cmp := Big(vm.stack[args[ x ]]).Cmp( Big(vm.stack[args[ y ]]) ) |
|
|
|
|
|
|
|
// Set the result as "boolean" value to Rz
|
|
|
|
|
|
|
|
if cmp < 0 { // a < b
|
|
|
|
|
|
|
|
vm.stack[args[ z ]] = "1" |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
vm.stack[args[ z ]] = "0" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case oJMP: |
|
|
|
|
|
|
|
// Set the instruction pointer to the value at Rx
|
|
|
|
|
|
|
|
ptr, _ := strconv.Atoi( vm.stack[args[ x ]] ) |
|
|
|
|
|
|
|
nptr = ptr |
|
|
|
|
|
|
|
case oJMPI: |
|
|
|
|
|
|
|
// Set the instruction pointer to the value at Ry if Rx yields true
|
|
|
|
|
|
|
|
if vm.stack[args[ x ]] != "0" { |
|
|
|
|
|
|
|
ptr, _ := strconv.Atoi( vm.stack[args[ y ]] ) |
|
|
|
|
|
|
|
nptr = ptr |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
fmt.Println("Error op", op) |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if stPtr == nptr { |
|
|
|
|
|
|
|
stPtr++ |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
stPtr = nptr |
|
|
|
|
|
|
|
if Debug { fmt.Println("... JMP", nptr, "...") } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
*/ |
|
|
|
|
|
|
|