2e06a9e52d/vm/gonga/ngaro.go

User picture

Commiter: Charles Childers

Author: Charles Childers

Revision: 2e06a9e52d


File Size: 6.85 KB

(March 13, 2010 23:09 UTC) About 2 years ago

sync against gonga hg repo

 
Show/hide line numbers
// Original Ngaro Virtual Machine and Uki framework:
//	Copyright (C) 2008, 2009, 2010 Charles Childers
// Go port
//	Copyright 2009, 2010 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
	adds concurrency, floating point and direct IO support.

	Concurrency vocabulary:

	: :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 )

	Floating poing vocabulary:

	-2000
		dup : fpush	[ 5 + , ] ; dup : fpop	 [ 6 + , ] ;
		dup : f>jump	[ 10 + , ] ; dup : f<jump	 [ 11 + , ] ;
		dup : f!jump	[ 12 + , ] ; dup : f=jump	 [ 13 + , ] ;
		dup : f+	[ 16 + , ] ; dup : f-	[ 17 + , ] ;
		dup : f*	[ 18 + , ] ; dup : f/	[ 19 + , ] ;
	drop

	Direct IO vocabulary:

	 -1100 dup : >int? ( a-xf ) [ 28 + , ] ;	: . ( x- ) [ 29 + , ] ;
	 -1200 dup : >float? ( a-xf ) [ 28 + , ] ;	: f. ( x- ) [ 29 + , ] ;
	 -1300 dup : <accept ( ca- ) [ 28 + , ] ;	: $. ( a- ) [ 29 + , ] ;

	Direct IO usage:

	: tib<accept ( c- )
		dup 32 = whitespace @ and if drop -1 then tib <accept ;
	: (accept)
		dup tib<accept
		32 =if ( ugly way to know if this is the listen loop )
			tib >int? if .data rdrop listen ;then drop
			tib >float? if .data rdrop listen ;then drop
		then ;
	' (accept) is accept

	Example of a concurrent program:

	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"
	"strconv"
	"unsafe"
)

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

	FP       = -2000
	IOint    = -1100
	IOfloat  = -1200
	IOstring = -1300

	stackDepth = 100
	chanBuffer = 128
	nports     = 64
)

type NgaroVM struct {
	img     []int
	dump    string
	channel map[int]chan int
	Input   *Input
	Output
	EndOk chan bool
}

func ftoi(f float) int { return *(*int)(unsafe.Pointer(&f)) }
func itof(i int) float { return *(*float)(unsafe.Pointer(&i)) }

func (vm *NgaroVM) core(ip int) {
	var port [nports]int
	var sp, rsp int
	var tos int
	var data, addr [stackDepth + 2]int
	sp = 2 // to avoid underflows
	for ; ip < len(vm.img); ip++ {
		switch vm.img[ip] {
		case Nop:
		case Lit:
			sp++
			ip++
			data[sp] = vm.img[ip]
		case Dup:
			sp++
			data[sp] = tos
		case Drop:
			sp--
		case Swap:
			data[sp], data[sp-1] = data[sp-1], tos
		case Push:
			rsp++
			addr[rsp] = tos
			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] > tos {
				ip = vm.img[ip] - 1
			}
			sp = sp - 2
		case LtJump:
			ip++
			if data[sp-1] < tos {
				ip = vm.img[ip] - 1
			}
			sp = sp - 2
		case NeJump:
			ip++
			if data[sp-1] != tos {
				ip = vm.img[ip] - 1
			}
			sp = sp - 2
		case EqJump:
			ip++
			if data[sp-1] == tos {
				ip = vm.img[ip] - 1
			}
			sp = sp - 2
		case Fetch:
			data[sp] = vm.img[tos]
		case Store:
			vm.img[tos] = data[sp-1]
			sp = sp - 2
		case Add:
			data[sp-1] += tos
			sp--
		case Sub:
			data[sp-1] -= tos
			sp--
		case Mul:
			data[sp-1] *= tos
			sp--
		case Dinod:
			data[sp] = data[sp-1] / tos
			data[sp-1] = data[sp-1] % tos
		case And:
			data[sp-1] &= tos
			sp--
		case Or:
			data[sp-1] |= tos
			sp--
		case Xor:
			data[sp-1] ^= tos
			sp--
		case ShL:
			data[sp-1] <<= uint(tos)
			sp--
		case ShR:
			data[sp-1] >>= uint(tos)
			sp--
		case ZeroExit:
			if tos == 0 {
				sp--
				ip = addr[rsp]
				rsp--
			}
		case Inc:
			data[sp]++
		case Dec:
			data[sp]--
		case In:
			data[sp] = port[tos]
			port[tos] = 0
		case Out:
			port[0] = 0
			port[tos] = data[sp-1]
			sp = sp - 2
		case Wait:
			sp -= vm.wait(&port, tos, sp-2, rsp)

		case FP + GtJump:
			ip++
			if itof(data[sp-1]) > itof(tos) {
				ip = vm.img[ip] - 1
			}
			sp = sp - 2
		case FP + LtJump:
			ip++
			if itof(data[sp-1]) < itof(tos) {
				ip = vm.img[ip] - 1
			}
			sp = sp - 2
		case FP + Add:
			data[sp-1] = ftoi(itof(data[sp-1]) + itof(tos))
			sp--
		case FP + Sub:
			data[sp-1] = ftoi(itof(data[sp-1]) - itof(tos))
			sp--
		case FP + Mul:
			data[sp-1] = ftoi(itof(data[sp-1]) * itof(tos))
			sp--
		case FP + Dinod:
			data[sp-1] = ftoi(itof(data[sp-1]) / itof(tos))
			sp--

		case IOint + In:
			a := readA(vm.img, tos)
			sp++
			if i, err := strconv.Atoi(a); err != nil {
				data[sp] = 0
			} else {
				data[sp-1] = i
				data[sp] = -1
			}
		case IOint + Out:
			fmt.Fprint(vm.Output, tos)
			sp--
		case IOfloat + In:
			a := readA(vm.img, tos)
			sp++
			if f, err := strconv.Atof(a); err != nil {
				data[sp] = 0
			} else {
				data[sp-1] = ftoi(f)
				data[sp] = -1
			}
		case IOfloat + Out:
			fmt.Fprint(vm.Output, itof(tos))
			sp--
		case IOstring + In: // Accept
			var c [1]byte
			var cont func() bool
			if data[sp-1] == -1 {
				cont = func() bool { return c[0] != ' ' && c[0] != '\n' && c[0] != '\t' }
			} else {
				cont = func() bool { return c[0] != byte(data[sp-1]) }
			}
			_, err := vm.Input.Read(c[0:])
			// Skip leading white-space
			for data[sp-1] == -1 && (c[0] == ' ' || c[0] == '\n' || c[0] == '\t') {
				vm.Input.Read(c[0:])
			}
			a := tos
			for ; err == nil && cont(); _, err = vm.Input.Read(c[0:]) {
				vm.img[a] = int(c[0])
				a++
			}
			vm.img[a] = 0
			sp -= 2
		case IOstring + Out:
			for i := tos; i < len(vm.img); i++ {
				if vm.img[i] == 0 {
					break
				}
				fmt.Fprintf(vm.Output, "%c", byte(vm.img[i]))
			}
			sp--

		default:
			rsp++
			addr[rsp] = ip
			ip = vm.img[ip] - 1
		}
		// to avoid segfaults:
		if sp < 2 {
			sp = 2
		}
		tos = data[sp]
	}
}

func NewVM(img []int, dump string, in *Input, out Output) *NgaroVM {
	vm := NgaroVM{
		dump:    dump,
		channel: make(map[int]chan int),
		Input:   in,
		Output:  out,
		EndOk:   make(chan bool),
		img:     img,
	}
	go vm.core(0)
	return &vm
}