summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rwxr-xr-xas/asbin0 -> 2088979 bytes
-rw-r--r--as/as.go32
-rw-r--r--as/go.mod5
-rw-r--r--as/go.sum (renamed from node/go.sum)0
-rw-r--r--as/main.go59
-rw-r--r--as/opcodes.go101
-rw-r--r--node/node.go15
-rw-r--r--test/add.easm4
-rw-r--r--vm/go.mod (renamed from node/go.mod)0
-rw-r--r--vm/go.sum2
-rw-r--r--vm/instruction.go147
-rw-r--r--vm/instructions.go3
-rw-r--r--vm/main.go26
-rw-r--r--vm/memory.go39
-rw-r--r--vm/opcodes.go57
-rw-r--r--vm/rom.go19
-rw-r--r--vm/stack.go (renamed from node/stack.go)12
-rw-r--r--vm/vm.go45
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
+```
diff --git a/as/as b/as/as
new file mode 100755
index 0000000..3c80b92
--- /dev/null
+++ b/as/as
Binary files differ
diff --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/node/go.sum b/as/go.sum
index e0f03a0..e0f03a0 100644
--- a/node/go.sum
+++ b/as/go.sum
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/node/go.mod b/vm/go.mod
index 725da6d..725da6d 100644
--- a/node/go.mod
+++ b/vm/go.mod
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)
+}