├── .gitattributes ├── .gitignore ├── README.md ├── examples └── helloworld.foo ├── foovm.go └── main └── main.go /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | # Sublime 50 | *.sublime-project 51 | *.sublime-workspace 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FooVM 2 | 3 | A Simple Bytecode Runtime 4 | 5 | `go get github.com/apkrieg/foovm` 6 | 7 | ### Description 8 | This runtime was built at part of a contest with [circle601](https://github.com/circle601) to see which runtime could run a set of example applications the fastest. 9 | 10 | This runtime was the winner as it was the only runtime that could pass all the tests. 11 | 12 | ### Update 13 | 14 | This project hasn't been updated in a while and I don't think I will update it. I've kinda stepped away from Go because I don't like what Google has been doing with the language. I've started to use Rust more and more as my daily driver and have started development on FizzVM which is written in Rust and Assembly. 15 | 16 | ### Hello World! 17 | ``` 18 | push 16 ; string heap index 19 | push 0 ; string heap segment 20 | push 12 ; string length 21 | push 0 ; print call index 22 | push 255 ; runtime call segment 23 | call ; call print 24 | push 1 ; exit call index 25 | push 255 ; runtime call segment 26 | call ; call exit 27 | "Hello World!" ; "Hello World!" string 28 | ``` 29 | 30 | ### Bytecode Reference 31 | Instruction | Mnemonic | Op Code | Description 32 | ----------- | -------- | ------- | ----------- 33 | Nil | nil | 0x00 | Nil pointer/no-op 34 | Push | push | 0x01 | *stack* <- data 35 | Pop | pop | 0x02 | <- *stack* 36 | Load | load | 0x03 | Load data from heap to stack 37 | Store | store | 0x04 | store data from stack to heap 38 | Add | add | 0x05 | Add pop1 + pop2 39 | Subtract | sub | 0x06 | Subtract pop1 - pop2 40 | Multiply | mul | 0x07 | Multiply pop1 * pop2 41 | Divide | div | 0x08 | Divide pop1 / pop2 42 | Call | call | 0x09 | Call a function 43 | Return | ret | 0x0a | Return from function 44 | Jump | jmp | 0x0b | Jump to address 45 | Compare | cmp | 0x0c | Compare two values 46 | Jump if Equal | jeq | 0x0d | Jump if a == b 47 | Jump if not Equal | jne | 0x0e | Jump if a != b 48 | Jump if Greater Than | jgt | 0x0f | Jump if a > b 49 | Jump if Less Than | jlt | 0x10 | Jump if a < b 50 | Jump if Less Than or Equal | jle | 0x11 | Jump if a <= b 51 | Jump if Greater Than or Equal | jge | 0x12 | Jump if a >= b 52 | -------------------------------------------------------------------------------- /examples/helloworld.foo: -------------------------------------------------------------------------------- 1 | .segment "home" 2 | 3 | ;loop 4 | push 1 5 | push 1 6 | load ;load max_counter 7 | push 0 8 | push 1 9 | load ;load counter 10 | push # 11 | push # 12 | jge ; 13 | 14 | 15 | ;exit 16 | push 0x01 17 | push 0xff 18 | call 19 | 20 | .segment "data" 21 | 0 ;counter 22 | 12 ;max_counter 23 | Hello World! ;string 24 | -------------------------------------------------------------------------------- /foovm.go: -------------------------------------------------------------------------------- 1 | package foovm 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Instructions 8 | const ( 9 | Nil byte = iota // 0x00 10 | Push byte = iota // 0x01 11 | Pop byte = iota // 0x02 12 | Load byte = iota // 0x03 13 | Store byte = iota // 0x04 14 | Add byte = iota // 0x05 15 | Sub byte = iota // 0x06 16 | Mul byte = iota // 0x07 17 | Div byte = iota // 0x08 18 | Call byte = iota // 0x09 19 | Ret byte = iota // 0x0a 20 | Jmp byte = iota // 0x0b 21 | Cmp byte = iota // 0x0c 22 | Jeq byte = iota // 0x0d 23 | Jne byte = iota // 0x0e 24 | Jgt byte = iota // 0x0f 25 | Jlt byte = iota // 0x10 26 | Jle byte = iota // 0x11 27 | Jge byte = iota // 0x12 28 | ) 29 | 30 | type FooVM struct { 31 | Cmp1 byte 32 | Cmp2 byte 33 | // Ret Array 34 | Rets []uint16 35 | // 256 B Stack 36 | RSP uint16 37 | Stack []byte 38 | // 64 KB Heap 39 | RHP uint16 40 | Heap []byte 41 | } 42 | 43 | func New() *FooVM { 44 | fvm := new(FooVM) 45 | fvm.Rets = make([]uint16, 0) 46 | fvm.Stack = make([]byte, 256) 47 | fvm.Heap = make([]byte, 256*256) 48 | return fvm 49 | } 50 | 51 | func PrintDebug(fvm *FooVM) { 52 | fmt.Println("\n\nDebug:") 53 | fmt.Printf("RSP=%#x, RHP=%#x", fvm.RSP, fvm.RHP) 54 | fmt.Println("\n\nStack:") 55 | for i, v := range fvm.Stack { 56 | if i%16 == 0 && i != 0 { 57 | fmt.Println("") 58 | } 59 | fmt.Printf("%#x ", v) 60 | } 61 | fmt.Println("\n\nHeap:") 62 | for i, v := range fvm.Heap { 63 | if i%32 == 0 && i != 0 { 64 | fmt.Println("") 65 | } 66 | if i%256 == 0 && i != 0 { 67 | fmt.Println("-----") 68 | } 69 | if i == 8*256 { 70 | break 71 | } 72 | fmt.Printf("%#x ", v) 73 | } 74 | return 75 | } 76 | 77 | // Execute bytecode 78 | func (fvm *FooVM) Exec() { 79 | for { 80 | switch inst := fvm.Heap[fvm.RHP]; { 81 | case inst == Nil: 82 | fvm.RHP++ 83 | break 84 | case inst == Push: 85 | fvm.Stack[fvm.RSP] = fvm.Heap[fvm.RHP+1] 86 | fvm.RSP++ 87 | fvm.RHP += 2 88 | break 89 | case inst == Pop: 90 | fvm.RSP-- 91 | fvm.RHP++ 92 | break 93 | case inst == Load: 94 | fvm.Stack[fvm.RSP] = fvm.Heap[uint16(fvm.Stack[fvm.RSP-1])*256+uint16(fvm.Stack[fvm.RSP-2])] 95 | fvm.RSP -= 2 96 | fvm.RHP++ 97 | break 98 | case inst == Store: 99 | fvm.Heap[uint16(fvm.Stack[fvm.RSP-1])*256+uint16(fvm.Stack[fvm.RSP-2])] = fvm.Stack[fvm.RSP-3] 100 | fvm.RSP -= 3 101 | fvm.RHP++ 102 | break 103 | case inst == Add: 104 | fvm.Stack[fvm.RSP-2] = fvm.Stack[fvm.RSP-1] + fvm.Stack[fvm.RSP-2] 105 | fvm.RSP-- 106 | fvm.RHP++ 107 | break 108 | case inst == Sub: 109 | fvm.Stack[fvm.RSP-2] = fvm.Stack[fvm.RSP-1] - fvm.Stack[fvm.RSP-2] 110 | fvm.RSP-- 111 | fvm.RHP++ 112 | break 113 | case inst == Mul: 114 | fvm.Stack[fvm.RSP-2] = fvm.Stack[fvm.RSP-1] * fvm.Stack[fvm.RSP-2] 115 | fvm.RSP-- 116 | fvm.RHP++ 117 | break 118 | case inst == Div: 119 | fvm.Stack[fvm.RSP-2] = fvm.Stack[fvm.RSP-1] / fvm.Stack[fvm.RSP-2] 120 | fvm.RSP-- 121 | fvm.RHP++ 122 | break 123 | case inst == Call: 124 | fvm.Rets = append(fvm.Rets, fvm.RHP) 125 | if fvm.Stack[fvm.RSP-1] == 0xff { 126 | if fvm.Stack[fvm.RSP-2] == 0x00 { // Print 127 | temp := uint16(fvm.Stack[fvm.RSP-4])*256 + uint16(fvm.Stack[fvm.RSP-5]) 128 | for _, v := range fvm.Heap[temp : temp+uint16(fvm.Stack[fvm.RSP-3])] { 129 | fmt.Printf("%c", v) 130 | } 131 | fvm.RSP -= 5 132 | fvm.RHP++ 133 | } else if fvm.Stack[fvm.RSP-2] == 0x01 { // Exit 134 | return 135 | } 136 | } else { 137 | fvm.RHP = uint16(fvm.Stack[fvm.RSP-1])*256 + uint16(fvm.Stack[fvm.RSP-2]) 138 | fvm.RSP -= 2 139 | } 140 | break 141 | case inst == Ret: 142 | fvm.RHP = fvm.Rets[len(fvm.Rets)-1] 143 | fvm.Rets = fvm.Rets[0:len(fvm.Rets)-1] 144 | fvm.RHP++ 145 | break 146 | case inst == Jmp: 147 | fvm.RHP = uint16(fvm.Stack[fvm.RSP-1])*256 + uint16(fvm.Stack[fvm.RSP-2]) 148 | fvm.RSP -= 2 149 | break 150 | case inst == Cmp: 151 | fvm.Cmp1 = fvm.Stack[fvm.RSP-1] 152 | fvm.Cmp2 = fvm.Stack[fvm.RSP-2] 153 | fvm.RSP -= 2 154 | fvm.RHP++ 155 | break 156 | case inst == Jeq: 157 | if fvm.Cmp1 == fvm.Cmp2 { 158 | fvm.RHP = uint16(fvm.Stack[fvm.RSP]-1)*256 + uint16(fvm.Stack[fvm.RSP-2]) 159 | } 160 | fvm.RSP -= 2 161 | break 162 | case inst == Jne: 163 | if fvm.Cmp1 != fvm.Cmp2 { 164 | fvm.RHP = uint16(fvm.Stack[fvm.RSP]-1)*256 + uint16(fvm.Stack[fvm.RSP-2]) 165 | } 166 | fvm.RSP -= 2 167 | break 168 | case inst == Jgt: 169 | if fvm.Cmp1 > fvm.Cmp2 { 170 | fvm.RHP = uint16(fvm.Stack[fvm.RSP]-1)*256 + uint16(fvm.Stack[fvm.RSP-2]) 171 | } 172 | fvm.RSP -= 2 173 | break 174 | case inst == Jlt: 175 | if fvm.Cmp1 < fvm.Cmp2 { 176 | fvm.RHP = uint16(fvm.Stack[fvm.RSP]-1)*256 + uint16(fvm.Stack[fvm.RSP-2]) 177 | } 178 | fvm.RSP -= 2 179 | break 180 | case inst == Jle: 181 | if fvm.Cmp1 <= fvm.Cmp2 { 182 | fvm.RHP = uint16(fvm.Stack[fvm.RSP]-1)*256 + uint16(fvm.Stack[fvm.RSP-2]) 183 | } 184 | fvm.RSP -= 2 185 | break 186 | case inst == Jge: 187 | if fvm.Cmp1 >= fvm.Cmp2 { 188 | fvm.RHP = uint16(fvm.Stack[fvm.RSP]-1)*256 + uint16(fvm.Stack[fvm.RSP-2]) 189 | } 190 | fvm.RSP -= 2 191 | break 192 | default: 193 | return 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | fvm "github.com/apkrieg/foovm" 5 | ) 6 | 7 | var ( 8 | Program = []byte{ 9 | fvm.Push, 16, 10 | fvm.Push, 0, 11 | fvm.Push, 12, 12 | fvm.Push, 0, 13 | fvm.Push, 255, 14 | fvm.Call, 15 | fvm.Push, 1, 16 | fvm.Push, 255, 17 | fvm.Call, 18 | 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', 19 | } 20 | ) 21 | 22 | func main() { 23 | vm := fvm.New() 24 | for i, v := range Program { 25 | vm.Heap[i] = v 26 | } 27 | vm.Exec() 28 | } 29 | --------------------------------------------------------------------------------