fbec8f49ae/vm/golang/ngaro.go
Commiter: Charles Childers
Author: Charles Childers
Revision: fbec8f49ae
File Size: 8.58 KB
(February 15, 2010 01:11 UTC) Over 2 years ago
sync against hg repo for golang vm
// Original Ngaro Virtual Machine and Uki framework:
// Copyright (C) 2008, 2009, Charles Childers
// Go port
// Copyright 2009 JGL
// Public Domain or LICENSE file
/*
Ngaro virtual machines.
Ngaro is a portable virtual machine / emulator for a dual
stack processor and various I/O devices. The instruction set
is concise (31 core instructions), and the basic I/O devices
are kept minimal. For more information see
http://github.com/crcx/ngaro
Communication with the virtual machine is done through
int chanels for input (port 1) and output (port 2). The
port 4 can be used to save the current image to a file,
while port 5 is used to get information about the virtual
machine.
In addition to normal ngaro features, this Go version
allows to launch new cores writing 1 to the port 13. The
new core will start running the same (shared) image
from the address at the top of the stack. Cores communicate
writing and id to port 1 (to receive) and 2 (to send). To
remove a channel write its id to ports 1 and 2 and wait.
Some useful words:
: :go ( a- ) 1 13 out wait ; ( start new core with ip set to a )
: go ( "- ) ' :go ; ( parse a word and run it on a new core )
: ->c ( xy- ) 2 out wait ; ( send x to channel y )
: c-> ( x-y ) 1 out wait 1 in ; ( receive y from channel x )
: delchan ( x- ) dup 1 out 2 out wait ; ( delete channel )
: cores ( -x ) -7 5 out wait 5 in ; ( number of running cores )
: chans ( -x ) -8 5 out wait 5 in ; ( number of channels in use )
Example:
create ch1 ( It is not needed to create channels, but a )
create ch2 ( good way to get a semi-random new id )
: odd ( - ) 1 repeat dup ch1 ->c 2 + again ;
: even ( - ) 2 repeat dup ch2 ->c 2 + again ;
go odd
go even
ch1 c-> . ch1 c-> . ch1 c-> . ( prints: 1 3 5 )
ch2 c-> . ch2 c-> . ch2 c-> . ( prints: 2 4 6 )
ch1 delchan ( clean up )
ch2 delchan
Usage from go: See gonga.go
*/
package ngaro
import (
"fmt"
"os"
"bufio"
B "encoding/binary"
)
const (
// Instruction set
Nop = iota
Lit
Dup
Drop
Swap
Push
Pop
Call
Jump
Return
GtJump
LtJump
NeJump
EqJump
Fetch
Store
Add
Sub
Mul
Dinod
And
Or
Xor
ShL
ShR
ZeroExit
Inc
Dec
In
Out
Wait
stackDepth = 100
chanBuffer = 128
nports = 64
)
type NgaroVM struct {
cores int
size int
img []int
dump string
channel map[int]chan int
EOI chan bool
OutputDone chan bool
Off chan bool
}
var Verbose bool = false
var ClearScreen func() = func() {}
func perror(s ...interface{}) {
if Verbose {
fmt.Fprint(os.Stderr, s)
}
}
func (vm *NgaroVM) Read(p []byte) (n int, err os.Error) {
for i, _ := range p {
if x := <-vm.channel[1]; x < 0 {
ClearScreen()
} else {
p[i] = byte(x)
n++
}
}
return
}
func (vm *NgaroVM) Write(p []byte) (n int, err os.Error) {
for _, b := range p {
vm.channel[0] <- int(b)
n++
}
return
}
func LoadDump(filename string, img []int, start int) int {
r, err := os.Open(filename, os.O_RDONLY, 0)
if err != nil {
return 0
}
br := bufio.NewReader(r)
// Skip header (shebang and lines starting with #)
for c, err := br.ReadByte(); err == nil && c == '#'; c, err = br.ReadByte() {
br.ReadBytes('\n')
}
br.UnreadByte()
var ui uint32
var i int
for i, _ = range img[start:] {
if err := B.Read(br, B.LittleEndian, &ui); err != nil {
break
}
img[i] = int(ui)
}
perror(" [ Ngaro: loaded image ", filename, " ] ")
return i
}
func (vm *NgaroVM) WriteDump(filename string) {
w, err := os.Open(filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return
}
for _, i := range vm.img {
if err = B.Write(w, B.LittleEndian, uint32(i)); err != nil {
perror(" [ Ngaro ERROR: writing ", filename, "] ")
}
}
perror(" [ Ngaro: saved image to ", filename, " ( size:", vm.size, " ) ] ")
}
func (vm *NgaroVM) Channel(id int) chan int {
// Returns a channel that can be used by any core. Channels 0 and
// 1 are reserved as input and output, any other id can be used
if c, ok := vm.channel[id]; ok {
return c
}
vm.channel[id] = make(chan int)
return vm.channel[id]
}
func (vm *NgaroVM) wait(port *[nports]int, tos int) (spdec int) {
if port[0] == 1 {
return
}
if port[1] > 1 {
if port[2] == port[1] {
vm.channel[port[1]] = nil, false
port[1] = 0
port[2] = 0
} else {
port[1] = <-vm.Channel(port[1])
}
port[0] = 1
} else if port[2] > 1 {
vm.Channel(port[2]) <- tos
port[2] = 0
port[0] = 1
spdec = 1
}
switch 1 {
case port[0]:
return
case port[1]: // Input (Port 1)
var p int
var eoi bool
if !eoi {
select {
case p = <-vm.channel[0]:
port[1] = p
case <-vm.EOI:
eoi = true
}
}
if eoi {
var ok bool
port[1], ok = <-vm.channel[0]
if !ok {
vm.Off <- true
}
}
port[0] = 1
case port[2]: // Output (Port 2)
vm.channel[1] <- tos
<-vm.OutputDone
port[2] = 0
port[0] = 1
spdec = 1
case port[4]: // Save Image (Port 4)
vm.WriteDump(vm.dump)
port[4] = 0
port[0] = 1
case port[13]: // New core (Port 13)
go vm.core(tos)
port[13] = 0
port[0] = 1
spdec = 1
}
switch port[5] { // Capabilities (Port 5)
case 0:
return
case -1: // Image size
port[5] = vm.size
port[0] = 1
case -5: // Stack depth
port[5] = stackDepth
port[0] = 1
case -6: // Address stack depth
port[5] = stackDepth
port[0] = 1
case -7: // Number of cores
port[5] = vm.cores
port[0] = 1
case -8: // Number of channels
port[5] = len(vm.channel)
port[0] = 1
default:
port[5] = 0
port[0] = 1
}
return
}
func (vm *NgaroVM) core(ip int) {
var x, y int
var port [nports]int
var sp, rsp int
var tos int
var data, addr [stackDepth]int
current := vm.cores
vm.cores++
for ; ip < vm.size; ip++ {
switch vm.img[ip] {
case Nop:
case Lit:
sp++
ip++
data[sp] = vm.img[ip]
case Dup:
sp++
data[sp] = data[sp-1]
case Drop:
data[sp] = 0
sp--
case Swap:
data[sp], data[sp-1] = data[sp-1], data[sp]
case Push:
rsp++
addr[rsp] = data[sp]
sp--
case Pop:
sp++
data[sp] = addr[rsp]
rsp--
case Call:
ip++
rsp++
addr[rsp] = ip
ip = vm.img[ip] - 1
case Jump:
ip++
ip = vm.img[ip] - 1
case Return:
ip = addr[rsp]
rsp--
case GtJump:
ip++
if data[sp-1] > data[sp] {
ip = vm.img[ip] - 1
}
sp = sp - 2
case LtJump:
ip++
if data[sp-1] < data[sp] {
ip = vm.img[ip] - 1
}
sp = sp - 2
case NeJump:
ip++
if data[sp-1] != data[sp] {
ip = vm.img[ip] - 1
}
sp = sp - 2
case EqJump:
ip++
if data[sp-1] == data[sp] {
ip = vm.img[ip] - 1
}
sp = sp - 2
case Fetch:
data[sp] = vm.img[data[sp]]
case Store:
vm.img[data[sp]] = data[sp-1]
sp = sp - 2
case Add:
data[sp-1] += data[sp]
data[sp] = 0
sp--
case Sub:
data[sp-1] -= data[sp]
data[sp] = 0
sp--
case Mul:
data[sp-1] *= data[sp]
data[sp] = 0
sp--
case Dinod:
x = data[sp]
y = data[sp-1]
data[sp] = y / x
data[sp-1] = y % x
case And:
x = data[sp]
y = data[sp-1]
sp--
data[sp] = x & y
case Or:
x = data[sp]
y = data[sp-1]
sp--
data[sp] = x | y
case Xor:
x = data[sp]
y = data[sp-1]
sp--
data[sp] = x ^ y
case ShL:
x = data[sp]
y = data[sp-1]
sp--
data[sp] = y << uint(x)
case ShR:
x = data[sp]
y = data[sp-1]
sp--
data[sp] = y >> uint(x)
case ZeroExit:
if data[sp] == 0 {
sp--
ip = addr[rsp]
rsp--
}
case Inc:
data[sp]++
case Dec:
data[sp]--
case In:
if data[sp] < 0 || data[sp] > nports-1 {
perror(" [ Ngaro ERROR: Invalid port ] ")
break
}
x = data[sp]
data[sp] = port[x]
port[x] = 0
case Out:
if data[sp] < 0 || data[sp] > nports-1 {
perror(" [ Ngaro ERROR: Invalid port ] ")
break
}
port[0] = 0
port[data[sp]] = data[sp-1]
sp = sp - 2
case Wait:
sp -= vm.wait(&port, tos)
default:
ip = vm.size
}
// to avoid segfaults:
if sp < 0 {
perror(" [ Ngaro ERROR: Stack underflow ] ")
sp = 0
} else if stackDepth-sp < 2 {
perror(" [ Ngaro ERROR: Stack overflow (2 elements droped) ] ")
sp -= 2
}
tos = data[sp]
}
vm.cores--
if current == 0 {
vm.Off <- true
}
}
func NewVM(image []int, size int, dump string) *NgaroVM {
if len(image) > size {
perror(" [ Ngaro ERROR: image too large ] ")
return nil
}
vm := new(NgaroVM)
vm.size = size
vm.dump = dump
vm.channel = make(map[int]chan int)
vm.channel[0] = make(chan int, chanBuffer)
vm.channel[1] = make(chan int, chanBuffer)
vm.EOI = make(chan bool)
vm.OutputDone = make(chan bool)
vm.Off = make(chan bool)
vm.img = make([]int, vm.size)
for i, x := range image {
vm.img[i] = x
}
perror(" [ Ngaro: image loaded ( size:", size, ") ] ")
go vm.core(0)
return vm
} |