go/ngaro

log tags | changeset raw browse | file diff annotate file log raw

view ngaro.go @ 51:b7d5185d0cf0

fixes. float enabled accept
author yiyus@1936
date Sun Feb 21 20:48:43 2010 +0100 (2010-02-21 ago)
parents 1be377666ed9
children

1 // Original Ngaro Virtual Machine and Uki framework:
2 // Copyright (C) 2008, 2009, Charles Childers
3 // Go port
4 // Copyright 2009 JGL
5 // Public Domain or LICENSE file
7 /*
8 Ngaro virtual machines.
10 Ngaro is a portable virtual machine / emulator for a dual stack
11 processor and various I/O devices. The instruction set is concise
12 (31 core instructions), and the basic I/O devices are kept
13 minimal. For more information see
14 http://github.com/crcx/ngaro
16 Communication with the virtual machine is done through int
17 chanels for input (port 1) and output (port 2). The port 4 can
18 be used to save the current image to a file, while port 5 is
19 used to get information about the virtual machine.
21 In addition to normal ngaro features, this Go version allows to
22 launch new cores writing 1 to the port 13. The new core will
23 start running a copy of the current image from the address at
24 the top of the stack. Alternatively, writing -1 to the port,
25 the memory will be shared. Cores communicate writing and id to
26 port 1 (to receive) and 2 (to send). To remove a channel write
27 its id to ports 1 and 2 and wait.
29 Some useful words:
31 : :go ( a- ) 1 13 out wait ; ( start new core with ip set to a, img copy )
32 : go ( "- ) ' :go ; ( parse a word and run it on a new core, img copy )
33 : :spawn ( a- ) -1 13 out wait ; ( start new core with ip set to a )
34 : spawn ( "- ) ' :spawn ; ( parse a word and run it on a new core )
35 : cores ( -x ) -7 5 out wait 5 in ; ( number of running cores )
37 : <- ( xy- ) 2 out wait ; ( send x to channel y )
38 : -> ( x-y ) 1 out wait 1 in ; ( receive y from channel x )
39 : mkch ( "- ) create 0 , ; ( new semi-random id for a channel )
40 : delch ( x- ) dup 1 out 2 out wait ; ( delete channel )
41 : chans ( -x ) -8 5 out wait 5 in ; ( number of channels in use )
43 Example:
45 mkch ch1 ( it is not really needed to create channels )
46 mkch ch2
47 1 variable n
48 : <-inc ( x- ) repeat n @ over <- n ++ again ;
50 : inc1 ch1 <-inc ;
51 go inc1
52 ch1 -> . ch1 -> . n @ . cr ( prints: 0 1 0 )
54 : inc2 ch2 <-inc ;
55 spawn inc2
56 ch2 -> . ch2 -> . n @ . cr ( prints: 0 1 2 )
58 delch ch1 ( clean up )
59 delch ch2
61 Usage from go: See gonga.go
63 */
65 package ngaro
67 import (
68 "os"
69 "io"
70 "bufio"
71 B "encoding/binary"
72 )
74 const (
75 // Instruction set
76 Nop = iota
77 Lit
78 Dup
79 Drop
80 Swap
81 Push
82 Pop
83 Call
84 Jump
85 Return
86 GtJump
87 LtJump
88 NeJump
89 EqJump
90 Fetch
91 Store
92 Add
93 Sub
94 Mul
95 Dinod
96 And
97 Or
98 Xor
99 ShL
100 ShR
101 ZeroExit
102 Inc
103 Dec
104 In
105 Out
106 Wait
108 stackDepth = 100
109 chanBuffer = 1
110 nports = 64
111 )
113 type CoreState struct {
114 img, data, addr []int
115 ip, sp, rsp *int
116 ports []int
117 }
119 type Extension interface {
120 Id() int
121 Process(CoreState)
122 }
124 type NgaroVM struct {
125 cores int
126 size int
127 img []int
128 dump string
129 channel map[int]chan int
130 ext []Extension
131 in io.Reader
132 out io.Writer
133 EndOk chan bool
134 }
136 var Verbose bool = false
137 var PrintError func(...interface{}) = func(...interface{}) {}
138 var ClearScreen func() = func() {}
140 func LoadDump(filename string, img []int, start int) int {
141 r, err := os.Open(filename, os.O_RDONLY, 0)
142 if err != nil {
143 return 0
144 }
145 br := bufio.NewReader(r)
146 // Skip header (shebang and lines starting with #)
147 for c, err := br.ReadByte(); err == nil && c == '#'; c, err = br.ReadByte() {
148 br.ReadBytes('\n')
149 }
150 br.UnreadByte()
151 var ui uint32
152 var i int
153 for i, _ = range img[start:] {
154 if err := B.Read(br, B.LittleEndian, &ui); err != nil {
155 break
156 }
157 img[i] = int(ui)
158 }
159 PrintError(" [ Ngaro: loaded image ", filename, " ] ")
160 return i
161 }
163 func (vm *NgaroVM) WriteDump(filename string) {
164 w, err := os.Open(filename, os.O_WRONLY|os.O_CREATE, 0666)
165 if err != nil {
166 return
167 }
168 for _, i := range vm.img {
169 if err = B.Write(w, B.LittleEndian, uint32(i)); err != nil {
170 PrintError(" [ Ngaro ERROR: writing ", filename, "] ")
171 }
172 }
173 PrintError(" [ Ngaro: saved image to ", filename, " ( size:", vm.size, " ) ] ")
174 }
176 func (vm *NgaroVM) Channel(id int) chan int {
177 // Returns a channel that can be used by any core. Channels 0 and
178 // 1 are reserved as input and output, any other id can be used
179 if c, ok := vm.channel[id]; ok {
180 return c
181 }
182 vm.channel[id] = make(chan int)
183 return vm.channel[id]
184 }
186 func (vm *NgaroVM) wait(port *[nports]int, tos int) (spdec int) {
187 var c [1]byte
188 if port[0] == 1 {
189 return
190 }
191 if port[1] > 1 {
192 if port[2] == port[1] {
193 vm.channel[port[1]] = nil, false
194 port[1] = 0
195 port[2] = 0
196 } else {
197 port[1] = <-vm.Channel(port[1])
198 }
199 port[0] = 1
200 } else if port[2] > 1 {
201 vm.Channel(port[2]) <- tos
202 port[2] = 0
203 port[0] = 1
204 spdec = 1
205 }
206 switch 1 {
207 case port[0]:
208 return
209 case port[1]: // Input (Port 1)
210 if _, err := vm.in.Read(c[0:]); err != nil {
211 vm.EndOk <- false
212 return
213 }
214 port[1] = int(c[0])
215 port[0] = 1
217 case port[2]: // Output (Port 2)
218 c[0] = byte(tos)
219 if tos < 0 {
220 ClearScreen()
221 } else if _, err := vm.out.Write(c[0:]); err != nil {
222 vm.EndOk <- false
223 return
224 }
225 port[2] = 0
226 port[0] = 1
227 spdec = 1
229 case port[4]: // Save Image (Port 4)
230 vm.WriteDump(vm.dump)
231 port[4] = 0
232 port[0] = 1
234 case port[13]: // New core (Port 13)
235 img := make([]int, vm.size)
236 copy(img, vm.img)
237 go vm.core(tos, img)
238 port[13] = 0
239 port[0] = 1
240 spdec = 1
242 case -port[13]: // New core (Port 13), shared mem
243 go vm.core(tos, vm.img)
244 port[13] = 0
245 port[0] = 1
246 spdec = 1
247 }
249 switch port[5] { // Capabilities (Port 5)
250 case 0:
251 return
253 case -1: // Image size
254 port[5] = vm.size
255 port[0] = 1
257 case -5: // Stack depth
258 port[5] = stackDepth
259 port[0] = 1
261 case -6: // Address stack depth
262 port[5] = stackDepth
263 port[0] = 1
265 case -7: // Number of cores
266 port[5] = vm.cores
267 port[0] = 1
269 case -8: // Number of channels
270 port[5] = len(vm.channel)
271 port[0] = 1
273 default:
274 port[5] = 0
275 port[0] = 1
276 }
277 return
278 }
280 func (vm *NgaroVM) core(ip int, img []int) {
281 var x, y int
282 var port [nports]int
283 var sp, rsp int
284 var tos int
285 var data, addr [stackDepth + 2]int
286 current := vm.cores
287 status := CoreState{img, data[0:], addr[0:], &ip, &sp, &rsp, port[0:]}
288 sp = 2 // Avoid stack underflows
289 vm.cores++
290 for ; ip < vm.size; ip++ {
291 InstructionSwitch: switch vm.img[ip] {
292 case Nop:
293 case Lit:
294 sp++
295 ip++
296 data[sp] = vm.img[ip]
297 case Dup:
298 sp++
299 data[sp] = data[sp-1]
300 case Drop:
301 data[sp] = 0
302 sp--
303 case Swap:
304 data[sp], data[sp-1] = data[sp-1], data[sp]
305 case Push:
306 rsp++
307 addr[rsp] = data[sp]
308 sp--
309 case Pop:
310 sp++
311 data[sp] = addr[rsp]
312 rsp--
313 case Call:
314 ip++
315 rsp++
316 addr[rsp] = ip
317 ip = vm.img[ip] - 1
318 case Jump:
319 ip++
320 ip = vm.img[ip] - 1
321 case Return:
322 ip = addr[rsp]
323 rsp--
324 case GtJump:
325 ip++
326 if data[sp-1] > data[sp] {
327 ip = vm.img[ip] - 1
328 }
329 sp = sp - 2
330 case LtJump:
331 ip++
332 if data[sp-1] < data[sp] {
333 ip = vm.img[ip] - 1
334 }
335 sp = sp - 2
336 case NeJump:
337 ip++
338 if data[sp-1] != data[sp] {
339 ip = vm.img[ip] - 1
340 }
341 sp = sp - 2
342 case EqJump:
343 ip++
344 if data[sp-1] == data[sp] {
345 ip = vm.img[ip] - 1
346 }
347 sp = sp - 2
348 case Fetch:
349 data[sp] = vm.img[data[sp]]
350 case Store:
351 vm.img[data[sp]] = data[sp-1]
352 sp = sp - 2
353 case Add:
354 data[sp-1] += data[sp]
355 data[sp] = 0
356 sp--
357 case Sub:
358 data[sp-1] -= data[sp]
359 data[sp] = 0
360 sp--
361 case Mul:
362 data[sp-1] *= data[sp]
363 data[sp] = 0
364 sp--
365 case Dinod:
366 x = data[sp]
367 y = data[sp-1]
368 data[sp] = y / x
369 data[sp-1] = y % x
370 case And:
371 x = data[sp]
372 y = data[sp-1]
373 sp--
374 data[sp] = x & y
375 case Or:
376 x = data[sp]
377 y = data[sp-1]
378 sp--
379 data[sp] = x | y
380 case Xor:
381 x = data[sp]
382 y = data[sp-1]
383 sp--
384 data[sp] = x ^ y
385 case ShL:
386 x = data[sp]
387 y = data[sp-1]
388 sp--
389 data[sp] = y << uint(x)
390 case ShR:
391 x = data[sp]
392 y = data[sp-1]
393 sp--
394 data[sp] = y >> uint(x)
395 case ZeroExit:
396 if data[sp] == 0 {
397 sp--
398 ip = addr[rsp]
399 rsp--
400 }
401 case Inc:
402 data[sp]++
403 case Dec:
404 data[sp]--
405 case In:
406 if data[sp] < 0 || data[sp] > nports-1 {
407 PrintError(" [ Ngaro ERROR: Invalid port ] ")
408 break
409 }
410 x = data[sp]
411 data[sp] = port[x]
412 port[x] = 0
413 case Out:
414 if data[sp] < 0 || data[sp] > nports-1 {
415 PrintError(" [ Ngaro ERROR: Invalid port ] ")
416 break
417 }
418 port[0] = 0
419 port[data[sp]] = data[sp-1]
420 sp = sp - 2
421 case Wait:
422 sp -= vm.wait(&port, tos)
423 default:
424 // Try unknown instructions with extensions
425 for _, ext := range (vm.ext) {
426 if vm.img[ip]&^0177 == ext.Id() {
427 ext.Process(status)
428 break InstructionSwitch
429 }
430 }
431 ip = vm.size
432 }
433 // to avoid segfaults:
434 if sp < 2 {
435 PrintError(" [ Ngaro ERROR: Stack underflow (elements droped) ] ")
436 sp = 2
437 } else if stackDepth-sp < 2 {
438 PrintError(" [ Ngaro ERROR: Stack overflow (elements droped) ] ")
439 sp -= 2
440 }
441 tos = data[sp]
442 }
443 vm.cores--
444 if current == 0 {
445 vm.EndOk <- true
446 }
447 }
449 func NewVM(image []int, size int, dump string, in io.Reader, out io.Writer, ext []Extension) *NgaroVM {
450 if len(image) > size {
451 PrintError(" [ Ngaro ERROR: image too large ] ")
452 return nil
453 }
454 img := make([]int, size)
455 size = copy(img, image)
456 vm := NgaroVM{
457 size: size,
458 dump: dump,
459 img: img,
460 in: in,
461 out: out,
462 ext: ext,
463 }
464 vm.channel = make(map[int]chan int)
465 vm.EndOk = make(chan bool)
466 PrintError(" [ Ngaro: image loaded ( size:", size, ") ] ")
467 go vm.core(0, vm.img)
468 return &vm
469 }