| | 1 | // Original Ngaro Virtual Machine and Uki framework: |
| | 2 | // Copyright (C) 2008, 2009, 2010 Charles Childers |
| | 3 | // Go port |
| | 4 | // Copyright 2009, 2010 JGL |
| | 5 | // Public Domain or LICENSE file |
| | 6 | |
| | 7 | /* |
| | 8 | Ngaro virtual machines. |
| | 9 | |
| | 10 | Ngaro is a portable virtual machine / emulator for a dual |
| | 11 | stack processor and various I/O devices. The instruction set |
| | 12 | is concise (31 core instructions), and the basic I/O devices |
| | 13 | are kept minimal. For more information see |
| | 14 | http://github.com/crcx/ngaro |
| | 15 | |
| | 16 | Communication with the virtual machine is done through |
| | 17 | int chanels for input (port 1) and output (port 2). The |
| | 18 | port 4 can be used to save the current image to a file, |
| | 19 | while port 5 is used to get information about the virtual |
| | 20 | machine. |
| | 21 | |
| | 22 | In addition to normal ngaro features, this Go version |
| | 23 | allows to launch new cores writing 1 to the port 13. The |
| | 24 | new core will start running the same (shared) image |
| | 25 | from the address at the top of the stack. Cores communicate |
| | 26 | writing and id to port 1 (to receive) and 2 (to send). To |
| | 27 | remove a channel write its id to ports 1 and 2 and wait. |
| | 28 | |
| | 29 | Some useful words: |
| | 30 | |
| | 31 | : :go ( a- ) 1 13 out wait ; ( start new core with ip set to a ) |
| | 32 | : go ( "- ) ' :go ; ( parse a word and run it on a new core ) |
| | 33 | : ->c ( xy- ) 2 out wait ; ( send x to channel y ) |
| | 34 | : c-> ( x-y ) 1 out wait 1 in ; ( receive y from channel x ) |
| | 35 | : delchan ( x- ) dup 1 out 2 out wait ; ( delete channel ) |
| | 36 | |
| | 37 | Example: |
| | 38 | |
| | 39 | create ch1 ( It is not needed to create channels, but a ) |
| | 40 | create ch2 ( good way to get a semi-random new id ) |
| | 41 | : odd ( - ) 1 repeat dup ch1 ->c 2 + again ; |
| | 42 | : even ( - ) 2 repeat dup ch2 ->c 2 + again ; |
| | 43 | go odd |
| | 44 | go even |
| | 45 | ch1 c-> . ch1 c-> . ch1 c-> . ( prints: 1 3 5 ) |
| | 46 | ch2 c-> . ch2 c-> . ch2 c-> . ( prints: 2 4 6 ) |
| | 47 | ch1 delchan ( clean up ) |
| | 48 | ch2 delchan |
| | 49 | |
| | 50 | Usage from go: See gonga.go |
| | 51 | |
| | 52 | */ |
| | 53 | |
| | 54 | package ngaro |
| | 55 | |
| | 56 | const ( |
| | 57 | // Instruction set |
| | 58 | Nop = iota |
| | 59 | Lit |
| | 60 | Dup |
| | 61 | Drop |
| | 62 | Swap |
| | 63 | Push |
| | 64 | Pop |
| | 65 | Call |
| | 66 | Jump |
| | 67 | Return |
| | 68 | GtJump |
| | 69 | LtJump |
| | 70 | NeJump |
| | 71 | EqJump |
| | 72 | Fetch |
| | 73 | Store |
| | 74 | Add |
| | 75 | Sub |
| | 76 | Mul |
| | 77 | Dinod |
| | 78 | And |
| | 79 | Or |
| | 80 | Xor |
| | 81 | ShL |
| | 82 | ShR |
| | 83 | ZeroExit |
| | 84 | Inc |
| | 85 | Dec |
| | 86 | In |
| | 87 | Out |
| | 88 | Wait |
| | 89 | |
| | 90 | stackDepth = 100 |
| | 91 | chanBuffer = 128 |
| | 92 | nports = 64 |
| | 93 | ) |
| | 94 | |
| | 95 | type NgaroVM struct { |
| | 96 | img []int |
| | 97 | dump string |
| | 98 | channel map[int]chan int |
| | 99 | Input *Input |
| | 100 | Output |
| | 101 | EndOk chan bool |
| | 102 | } |
| | 103 | |
| | 104 | func (vm *NgaroVM) core(ip int) { |
| | 105 | var port [nports]int |
| | 106 | var sp, rsp int |
| | 107 | var tos int |
| | 108 | var data, addr [stackDepth + 2]int |
| | 109 | sp = 2 // to avoid underflows |
| | 110 | for ; ip < len(vm.img); ip++ { |
| | 111 | switch vm.img[ip] { |
| | 112 | case Nop: |
| | 113 | case Lit: |
| | 114 | sp++ |
| | 115 | ip++ |
| | 116 | data[sp] = vm.img[ip] |
| | 117 | case Dup: |
| | 118 | sp++ |
| | 119 | data[sp] = tos |
| | 120 | case Drop: |
| | 121 | sp-- |
| | 122 | case Swap: |
| | 123 | data[sp], data[sp-1] = data[sp-1], tos |
| | 124 | case Push: |
| | 125 | rsp++ |
| | 126 | addr[rsp] = tos |
| | 127 | sp-- |
| | 128 | case Pop: |
| | 129 | sp++ |
| | 130 | data[sp] = addr[rsp] |
| | 131 | rsp-- |
| | 132 | case Call: |
| | 133 | ip++ |
| | 134 | rsp++ |
| | 135 | addr[rsp] = ip |
| | 136 | ip = vm.img[ip] - 1 |
| | 137 | case Jump: |
| | 138 | ip++ |
| | 139 | ip = vm.img[ip] - 1 |
| | 140 | case Return: |
| | 141 | ip = addr[rsp] |
| | 142 | rsp-- |
| | 143 | case GtJump: |
| | 144 | ip++ |
| | 145 | if data[sp-1] > tos { |
| | 146 | ip = vm.img[ip] - 1 |
| | 147 | } |
| | 148 | sp = sp - 2 |
| | 149 | case LtJump: |
| | 150 | ip++ |
| | 151 | if data[sp-1] < tos { |
| | 152 | ip = vm.img[ip] - 1 |
| | 153 | } |
| | 154 | sp = sp - 2 |
| | 155 | case NeJump: |
| | 156 | ip++ |
| | 157 | if data[sp-1] != tos { |
| | 158 | ip = vm.img[ip] - 1 |
| | 159 | } |
| | 160 | sp = sp - 2 |
| | 161 | case EqJump: |
| | 162 | ip++ |
| | 163 | if data[sp-1] == tos { |
| | 164 | ip = vm.img[ip] - 1 |
| | 165 | } |
| | 166 | sp = sp - 2 |
| | 167 | case Fetch: |
| | 168 | data[sp] = vm.img[tos] |
| | 169 | case Store: |
| | 170 | vm.img[tos] = data[sp-1] |
| | 171 | sp = sp - 2 |
| | 172 | case Add: |
| | 173 | data[sp-1] += tos |
| | 174 | sp-- |
| | 175 | case Sub: |
| | 176 | data[sp-1] -= tos |
| | 177 | sp-- |
| | 178 | case Mul: |
| | 179 | data[sp-1] *= tos |
| | 180 | sp-- |
| | 181 | case Dinod: |
| | 182 | data[sp] = data[sp-1] / tos |
| | 183 | data[sp-1] = data[sp-1] % tos |
| | 184 | case And: |
| | 185 | data[sp-1] &= tos |
| | 186 | sp-- |
| | 187 | case Or: |
| | 188 | data[sp-1] |= tos |
| | 189 | sp-- |
| | 190 | case Xor: |
| | 191 | data[sp-1] ^= tos |
| | 192 | sp-- |
| | 193 | case ShL: |
| | 194 | data[sp-1] <<= uint(tos) |
| | 195 | sp-- |
| | 196 | case ShR: |
| | 197 | data[sp-1] >>= uint(tos) |
| | 198 | sp-- |
| | 199 | case ZeroExit: |
| | 200 | if tos == 0 { |
| | 201 | sp-- |
| | 202 | ip = addr[rsp] |
| | 203 | rsp-- |
| | 204 | } |
| | 205 | case Inc: |
| | 206 | data[sp]++ |
| | 207 | case Dec: |
| | 208 | data[sp]-- |
| | 209 | case In: |
| | 210 | data[sp] = port[tos] |
| | 211 | port[tos] = 0 |
| | 212 | case Out: |
| | 213 | port[0] = 0 |
| | 214 | port[tos] = data[sp-1] |
| | 215 | sp = sp - 2 |
| | 216 | case Wait: |
| | 217 | sp -= vm.wait(&port, tos, ip) |
| | 218 | default: |
| | 219 | rsp++ |
| | 220 | addr[rsp] = ip |
| | 221 | ip = vm.img[ip] - 1 |
| | 222 | } |
| | 223 | // to avoid segfaults: |
| | 224 | if sp < 2 { |
| | 225 | sp = 2 |
| | 226 | } |
| | 227 | tos = data[sp] |
| | 228 | } |
| | 229 | } |
| | 230 | |
| | 231 | func NewVM(img []int, dump string, in *Input, out Output) *NgaroVM { |
| | 232 | vm := NgaroVM{ |
| | 233 | dump: dump, |
| | 234 | channel: make(map[int]chan int), |
| | 235 | Input: in, |
| | 236 | Output: out, |
| | 237 | EndOk: make(chan bool), |
| | 238 | img: img, |
| | 239 | } |
| | 240 | go vm.core(0) |
| | 241 | return &vm |
| | 242 | } |