diff options
-rw-r--r-- | README.md | 10 | ||||
-rwxr-xr-x | as/as | bin | 0 -> 2088979 bytes | |||
-rw-r--r-- | as/as.go | 32 | ||||
-rw-r--r-- | as/go.mod | 5 | ||||
-rw-r--r-- | as/go.sum (renamed from node/go.sum) | 0 | ||||
-rw-r--r-- | as/main.go | 59 | ||||
-rw-r--r-- | as/opcodes.go | 101 | ||||
-rw-r--r-- | node/node.go | 15 | ||||
-rw-r--r-- | test/add.easm | 4 | ||||
-rw-r--r-- | vm/go.mod (renamed from node/go.mod) | 0 | ||||
-rw-r--r-- | vm/go.sum | 2 | ||||
-rw-r--r-- | vm/instruction.go | 147 | ||||
-rw-r--r-- | vm/instructions.go | 3 | ||||
-rw-r--r-- | vm/main.go | 26 | ||||
-rw-r--r-- | vm/memory.go | 39 | ||||
-rw-r--r-- | vm/opcodes.go | 57 | ||||
-rw-r--r-- | vm/rom.go | 19 | ||||
-rw-r--r-- | vm/stack.go (renamed from node/stack.go) | 12 | ||||
-rw-r--r-- | vm/vm.go | 45 |
19 files changed, 555 insertions, 21 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..ab60e06 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +evm +=== + +building and running +-------------------- + +```sh +cd vm & go build +./evm +``` Binary files differdiff --git a/as/as.go b/as/as.go new file mode 100644 index 0000000..bcc7cf8 --- /dev/null +++ b/as/as.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "strings" + "github.com/holiman/uint256" + +) + +func Assemble(code string) []byte { + bytecode := make([]byte, 0) + + t := strings.Fields(code) + + for i := range t { + x, found := OpcodeStrings[t[i]] + + if found { + bytecode = append(bytecode, x) + } else { + var n *uint256.Int = nil + if len(t[i]) > 1 && t[i][1] == 'x' { + n, _ = uint256.FromHex(t[i]) + } else { + n, _ = uint256.FromDecimal(t[i]) + } + b := n.Bytes32() + bytecode = append(bytecode, b[:]...) + } + } + return bytecode +} diff --git a/as/go.mod b/as/go.mod new file mode 100644 index 0000000..6f40dd8 --- /dev/null +++ b/as/go.mod @@ -0,0 +1,5 @@ +module s00.xyz/as + +go 1.20 + +require github.com/holiman/uint256 v1.2.2 // indirect diff --git a/as/main.go b/as/main.go new file mode 100644 index 0000000..95aff46 --- /dev/null +++ b/as/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "os" + "fmt" +) + +func usage(arg0 string) { + fmt.Fprintf(os.Stderr, "usage: %s [-o file] file.easm\n", os.Args[0]) + os.Exit(1) +} + +func main() { + if (len(os.Args) <= 2) { + usage(os.Args[0]) + } + + + var aout int = 0 + var ain int = 0 + + for i := 0; i < len(os.Args); i++ { + fmt.Println(os.Args[i]) + switch os.Args[i] { + case "-o": + i = i + 1 + aout = i + default: + ain = i + } + } + + if (ain == 0) { + usage(os.Args[0]) + } + + var outf string = "out.bin" + if (aout != 0) { + outf = os.Args[aout] + } + + b, errin := os.ReadFile(os.Args[ain]) + out, errout := os.Create(outf) + + if errin != nil { + fmt.Fprintf(os.Stderr, "failed to read file: %s\n", os.Args[ain]) + os.Exit(1) + } + + if errout != nil { + fmt.Fprintf(os.Stderr, "failed to create file for output: %s\n", outf) + os.Exit(1) + } + + s := string(b) + c := Assemble(s) + + out.Write(c) +} diff --git a/as/opcodes.go b/as/opcodes.go new file mode 100644 index 0000000..94c2e2b --- /dev/null +++ b/as/opcodes.go @@ -0,0 +1,101 @@ +package main + +type Opcode byte + +const ( + STOP = iota + ADD + MUL + SUB + DIV + SDIV + MOD + SMOD + ADDMOD + MULMOD + POP = 0x50 + MLOAD = 0x51 + MSTORE = 0x52 + MSTORE8 = 0x53 + PUSH1 = 0x60 + PUSH2 = 0x61 + PUSH3 = 0x62 + PUSH4 = 0x63 + PUSH5 = 0x64 + PUSH6 = 0x65 + PUSH7 = 0x66 + PUSH8 = 0x67 + PUSH9 = 0x68 + PUSH10 = 0x69 + PUSH11 = 0x6a + PUSH12 = 0x6b + PUSH13 = 0x6c + PUSH14 = 0x6d + PUSH15 = 0x6e + PUSH16 = 0x6f + PUSH17 = 0x70 + PUSH18 = 0x71 + PUSH19 = 0x72 + PUSH20 = 0x73 + PUSH21 = 0x74 + PUSH22 = 0x75 + PUSH23 = 0x76 + PUSH24 = 0x77 + PUSH25 = 0x78 + PUSH26 = 0x79 + PUSH27 = 0x7a + PUSH28 = 0x7b + PUSH29 = 0x7c + PUSH30 = 0x7d + PUSH31 = 0x7e + PUSH32 = 0x7f +) + +var OpcodeStrings = map[string]byte { + "STOP": STOP, + "ADD": ADD, + "MUL": MUL, + "SUB": SUB, + "DIV": DIV, + "SDIV": SDIV, + "MOD": MOD, + "SMOD": SMOD, + "ADDMOD": ADDMOD, + "MULMOD": MULMOD, + "POP": POP, + "MLOAD": MLOAD, + "MSTORE": MSTORE, + "MSTORE8": MSTORE8, + "PUSH1": PUSH1, + "PUSH2": PUSH2, + "PUSH3": PUSH3, + "PUSH4": PUSH4, + "PUSH5": PUSH5, + "PUSH6": PUSH6, + "PUSH7": PUSH7, + "PUSH8": PUSH8, + "PUSH9": PUSH9, + "PUSH10": PUSH10, + "PUSH11": PUSH11, + "PUSH12": PUSH12, + "PUSH13": PUSH13, + "PUSH14": PUSH14, + "PUSH15": PUSH15, + "PUSH16": PUSH16, + "PUSH17": PUSH17, + "PUSH18": PUSH18, + "PUSH19": PUSH19, + "PUSH20": PUSH20, + "PUSH21": PUSH21, + "PUSH22": PUSH22, + "PUSH23": PUSH23, + "PUSH24": PUSH24, + "PUSH25": PUSH25, + "PUSH26": PUSH26, + "PUSH27": PUSH27, + "PUSH28": PUSH28, + "PUSH29": PUSH29, + "PUSH30": PUSH30, + "PUSH31": PUSH31, + "PUSH32": PUSH32, +} diff --git a/node/node.go b/node/node.go deleted file mode 100644 index 18640e9..0000000 --- a/node/node.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/holiman/uint256" -) - -func main() { - s := NewStack() - x, _ := uint256.FromHex("0xfeedface") - s.Push(x) - y := s.Pop() - fmt.Println(y.String()) -} diff --git a/test/add.easm b/test/add.easm new file mode 100644 index 0000000..4876b0b --- /dev/null +++ b/test/add.easm @@ -0,0 +1,4 @@ +PUSH 9 +PUSH 4 +ADD +STOP diff --git a/vm/go.sum b/vm/go.sum new file mode 100644 index 0000000..e0f03a0 --- /dev/null +++ b/vm/go.sum @@ -0,0 +1,2 @@ +github.com/holiman/uint256 v1.2.2 h1:TXKcSGc2WaxPD2+bmzAsVthL4+pEN0YwXcL5qED83vk= +github.com/holiman/uint256 v1.2.2/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= diff --git a/vm/instruction.go b/vm/instruction.go new file mode 100644 index 0000000..5cd56e6 --- /dev/null +++ b/vm/instruction.go @@ -0,0 +1,147 @@ +package main + +import ( + "github.com/holiman/uint256" +) + +type Handler func(vm *Evm) + +type Instruction struct { + name string + n int // number of params + handler Handler +} + +type Inst Instruction // shorthand + +// i miss c macros :( +var Instructions = [256]Instruction{ + STOP: { + name: "STOP", + n: 0, + handler: func(vm *Evm) { vm.stopped = true }, + } , + ADD: { + name: "ADD", + n: 2, + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Add(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + MUL: { + n: 2, + name: "MUL", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Mul(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + SUB: { + n: 2, + name: "SUB", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Sub(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + DIV: { + n: 2, + name: "DIV", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Div(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + SDIV: { + n: 2, + name: "SDIV", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.SDiv(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + MOD: { + n: 2, + name: "MOD", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.Mod(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + SMOD: { + n: 2, + name: "SMOD", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.SMod(vm.stack.Pop(), vm.stack.Pop())) + }, + }, + ADDMOD: { + n: 3, + name: "ADDMOD", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.AddMod(vm.stack.Pop(), vm.stack.Pop(), vm.stack.Pop())) + }, + }, + MULMOD: { + n: 3, + name: "MULMOD", + handler: func(vm *Evm) { + o := uint256.NewInt(0) + vm.stack.Push(o.MulMod(vm.stack.Pop(), vm.stack.Pop(), vm.stack.Pop())) + }, + }, + POP: { + n: 1, + name: "POP", + handler: func(vm *Evm) { + vm.stack.Pop() + }, + }, + MLOAD: { + n: 1, + name: "MLOAD", + }, + MSTORE: { + n: 2, + name: "MSTORE", + }, + MSTORE8: { + n: 2, + name: "MSTORE8", + }, + PUSH1: { name: "PUSH1" }, + PUSH2: { name: "PUSH2" }, + PUSH3: { name: "PUSH3" }, + PUSH4: { name: "PUSH4" }, + PUSH5: { name: "PUSH5" }, + PUSH6: { name: "PUSH6" }, + PUSH7: { name: "PUSH7" }, + PUSH8: { name: "PUSH8" }, + PUSH9: { name: "PUSH9" }, + PUSH10: { name: "PUSH0" }, + PUSH11: { name: "PUSH11" }, + PUSH12: { name: "PUSH12" }, + PUSH13: { name: "PUSH13" }, + PUSH14: { name: "PUSH14" }, + PUSH15: { name: "PUSH15" }, + PUSH16: { name: "PUSH16" }, + PUSH17: { name: "PUSH17" }, + PUSH18: { name: "PUSH18" }, + PUSH19: { name: "PUSH19" }, + PUSH20: { name: "PUSH20" }, + PUSH21: { name: "PUSH21" }, + PUSH22: { name: "PUSH22" }, + PUSH23: { name: "PUSH23" }, + PUSH24: { name: "PUSH24" }, + PUSH25: { name: "PUSH25" }, + PUSH26: { name: "PUSH26" }, + PUSH27: { name: "PUSH27" }, + PUSH28: { name: "PUSH28" }, + PUSH29: { name: "PUSH29" }, + PUSH30: { name: "PUSH30" }, + PUSH31: { name: "PUSH31" }, + PUSH32: { name: "PUSH32" }, +} diff --git a/vm/instructions.go b/vm/instructions.go new file mode 100644 index 0000000..5a1220b --- /dev/null +++ b/vm/instructions.go @@ -0,0 +1,3 @@ +package main + +type InstructionHandler func (vm *Evm) diff --git a/vm/main.go b/vm/main.go new file mode 100644 index 0000000..61aeebb --- /dev/null +++ b/vm/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + + if (len(os.Args) != 2) { + fmt.Fprintf(os.Stderr, "usage: %s bytecode.bin\n") + os.Exit(1) + } + + b, err := os.ReadFile(os.Args[1]) + + if err != nil { + fmt.Fprintf(os.Stderr, "failed to open file: %s\n", os.Args[1]) + os.Exit(1) + } + + vm := NewEvm(b) + + vm.Start() +} + diff --git a/vm/memory.go b/vm/memory.go new file mode 100644 index 0000000..0a9dd2c --- /dev/null +++ b/vm/memory.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/holiman/uint256" + "log" +) + +type Memory []byte + +// store 256b word at offset in memory +func (m *Memory) sw(ost uint64, val *uint256.Int) { + if ost + 32 > uint64(len(*m)) { + *m = append(*m, make([]byte, ost + 32 - uint64(len(*m)))...) + } + + bytes := val.Bytes32() + copy((*m)[ost:], bytes[:]) +} + +// store byte at offset in memory +func (m *Memory) sb(ost uint64, val byte) { + if ost + 1 > uint64(len(*m)) { + *m = append(*m, make([]byte, ost + 1 - uint64(len(*m)))...) + } + + (*m)[ost] = val +} + +// loads 256b word fron offset in memory +func (m *Memory) ld(ost uint64) []byte { + if ost + 32 > uint64(len(*m)) { + log.Fatal("trying to load out of memory") + } + + r := make([]byte, 32) + copy(r, (*m)[ost:ost+32]) + return r +} + diff --git a/vm/opcodes.go b/vm/opcodes.go new file mode 100644 index 0000000..b3d4229 --- /dev/null +++ b/vm/opcodes.go @@ -0,0 +1,57 @@ +package main + +type Opcode byte + +const ( + STOP = iota + ADD + MUL + SUB + DIV + SDIV + MOD + SMOD + ADDMOD + MULMOD + POP = 0x50 + MLOAD = 0x51 + MSTORE = 0x52 + MSTORE8 = 0x53 + PUSH1 = 0x60 + PUSH2 = 0x61 + PUSH3 = 0x62 + PUSH4 = 0x63 + PUSH5 = 0x64 + PUSH6 = 0x65 + PUSH7 = 0x66 + PUSH8 = 0x67 + PUSH9 = 0x68 + PUSH10 = 0x69 + PUSH11 = 0x6a + PUSH12 = 0x6b + PUSH13 = 0x6c + PUSH14 = 0x6d + PUSH15 = 0x6e + PUSH16 = 0x6f + PUSH17 = 0x70 + PUSH18 = 0x71 + PUSH19 = 0x72 + PUSH20 = 0x73 + PUSH21 = 0x74 + PUSH22 = 0x75 + PUSH23 = 0x76 + PUSH24 = 0x77 + PUSH25 = 0x78 + PUSH26 = 0x79 + PUSH27 = 0x7a + PUSH28 = 0x7b + PUSH29 = 0x7c + PUSH30 = 0x7d + PUSH31 = 0x7e + PUSH32 = 0x7f + //... +) + +func (op *Opcode) String() string { + return Instructions[*op].name +} diff --git a/vm/rom.go b/vm/rom.go new file mode 100644 index 0000000..958e70e --- /dev/null +++ b/vm/rom.go @@ -0,0 +1,19 @@ +package main + +import ( + "os" + "fmt" +) + +type Rom []byte + +func (r *Rom) Fetch(pc *uint64, n uint64) []byte { + if (*pc + n > uint64(len(*r)) - 1) { + fmt.Fprintf(os.Stderr, "error: trying to read outside of rom\n") + os.Exit(1) + } + x := make([]byte, n) + copy(x, (*r)[*pc:*pc + n]) + *pc += n + return x +} diff --git a/node/stack.go b/vm/stack.go index a5460a7..fc1cbd4 100644 --- a/node/stack.go +++ b/vm/stack.go @@ -3,20 +3,16 @@ package main import ( "log" - "github.com/holiman/uint256" + "github.com/holiman/uint256" ) const STACK_CAP = (1 << 10) type Stack []uint256.Int -func NewStack() *Stack { - return &Stack{} -} - func (s *Stack) Push(x *uint256.Int) { *s = append(*s, *x) - if len(*s) > STACK_CAP { + if len(*s) + 1 > STACK_CAP { log.Fatal("stack overflow") } } @@ -30,3 +26,7 @@ func (s *Stack) Pop() *uint256.Int { *s = (*s)[:len(*s)-1] return &r } + +func (s *Stack) Peek() *uint256.Int { + return &(*s)[len(*s)-1] +} diff --git a/vm/vm.go b/vm/vm.go new file mode 100644 index 0000000..209ab71 --- /dev/null +++ b/vm/vm.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "github.com/holiman/uint256" +) + +type Evm struct { + code Rom + stack Stack + memory Memory + pc uint64 + stopped bool +} + +func NewEvm(_code []byte) *Evm { + return &Evm{ + pc: 0, + stopped: true, + stack : Stack{}, + code: _code, + } +} + +func (vm *Evm) Start() { + vm.stopped = false + for !(vm.stopped) { + op := vm.code.Fetch(&(vm.pc), 1)[0] + fmt.Printf("pc: %d | opcode: %x -> string: %s\n", vm.pc, op, Instructions[op].name) + if op >= PUSH1 && op <= PUSH32 { + nb := op - PUSH1 + 1 + fmt.Printf("pushing %d byte value to the stack!\n", vm.pc, nb) + b := vm.code.Fetch(&(vm.pc), uint64(nb)) + x := uint256.NewInt(0) + x = x.SetBytes(b) + vm.stack.Push(x) + } else { + vm.Execute(op) + } + } +} + +func (vm *Evm) Execute(op byte) { + Instructions[op].handler(vm) +} |