Skip to content

Commit

Permalink
API changes
Browse files Browse the repository at this point in the history
  • Loading branch information
db47h committed Jul 27, 2016
1 parent ccc413a commit a028553
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 89 deletions.
8 changes: 5 additions & 3 deletions cmd/retro/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package main

import (
"bufio"
"flag"
"fmt"
"io"
Expand All @@ -35,8 +36,9 @@ func main() {
// default options
var optlist = []vm.Option{
vm.OptShrinkImage(*shrink),
vm.OptOutput(os.Stdout),
vm.OptInput(os.Stdin),
// buffered io is faster
vm.OptOutput(bufio.NewWriter(os.Stdout)),
vm.OptInput(bufio.NewReader(os.Stdin)),
}

// append withFile to the input stack
Expand All @@ -52,7 +54,7 @@ func main() {
img, err := vm.Load(*fileName, 1000000)
if err == nil {
proc := vm.New(img, *fileName, optlist...)
_, err = proc.Run(1000000)
err = proc.Run(len(proc.Image))
}
if err != nil {
switch errors.Cause(err) {
Expand Down
18 changes: 7 additions & 11 deletions vm/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package vm

import (
"fmt"
"io"
"os"
"time"
Expand All @@ -27,14 +26,19 @@ import (
"github.com/pkg/errors"
)

// flusher wraps the Flush method.
type flusher interface {
Flush() error
}

// readWriter wraps the WriteRune method. Works the same ad bufio.Writer.WriteRune.
type runeWriter interface {
WriteRune(r rune) (size int, err error)
}

// runeWriterWrapper wraps a plain io.Reader into a runeWriter and flusher.
// Flush() only works as a proxy for the wrapped Reader's own Flush method if
// implemented.
type runeWriterWrapper struct {
io.Writer
}
Expand All @@ -57,6 +61,7 @@ func (w *runeWriterWrapper) WriteRune(r rune) (size int, err error) {
return w.Writer.Write(b[0:l])
}

// Flush is only a proxy for the wrapped reader's own Flush method if implemented.
func (w *runeWriterWrapper) Flush() error {
if f, ok := w.Writer.(flusher); ok {
return f.Flush()
Expand Down Expand Up @@ -144,9 +149,6 @@ func (mr *multiRuneReader) ReadRune() (r rune, size int, err error) {

func (mr *multiRuneReader) pushReader(r io.Reader) {
mr.readers = append([]io.RuneReader{newRuneReader(r)}, mr.readers...)
for _, rr := range mr.readers {
fmt.Printf("%#v\n", rr)
}
}

// PushInput sets r as the current input RuneReader for the VM. When this reader
Expand All @@ -163,12 +165,6 @@ func (i *Instance) PushInput(r io.Reader) {
}
}

type breakError struct{}

func (breakError) Error() string {
return "user breakpoint"
}

func (i *Instance) out(v Cell, port int) {
i.ports[port] = v
i.ports[0] = 1
Expand Down Expand Up @@ -236,7 +232,7 @@ func (i *Instance) ioWait() error {
i.ports[5] = Cell(time.Now().Unix())
case -9:
i.ports[5] = 0
i.ip = len(i.Image) - 1 // will be incremented when returning
i.PC = len(i.Image) - 1 // will be incremented when returning
case -13:
i.ports[5] = Cell(unsafe.Sizeof(i.ports[0]) * 8)
case -16:
Expand Down
103 changes: 49 additions & 54 deletions vm/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,152 +44,150 @@ func (i *Instance) Rpop() Cell {
return i.address[rsp]
}

// Run starts execution of the VM until the intruction pointer reaches `toIP`.
// Returns current instruction pointer, i.e. the first instruction that will be
// executed on the next call to run. If an error occurs, ip will point to the
// instruction that triggered the error.
// Run starts execution of the VM until the program pointer reaches `toPC`.
// If an error occurs, the PC will will point to the instruction that triggered
// the error.
//
// If the VM was exited from a user program, ip will be equal to len(i.Image) and
// err will be nil.
func (i *Instance) Run(toIP int) (ip int, err error) {
// If the VM was exited cleanly from a user program with the `bye` word, the PC
// will be equal to len(i.Image) and err will be nil.
func (i *Instance) Run(toPC int) (err error) {
defer func() {
if e := recover(); e != nil {
err = errors.Errorf("%v", e)
ip = i.ip
}
}()
i.insCount = 0
for i.ip < toIP {
for i.PC < len(i.Image) {
// fmt.Printf("% 8d\t%s", p.ip, opcode)
// switch opcode {
// case OpLit, OpLoop, OpJump, OpGtJump, OpLtJump, OpEqJump, OpNeJump:
// fmt.Printf(" %d", int(p.Image[p.ip+1]))
// }
// fmt.Printf("\t%v\n", p.data[0:p.sp+1])
op := opcode(i.Image[i.ip])
op := opcode(i.Image[i.PC])
switch op {
case OpNop:
i.ip++
i.PC++
case OpLit:
i.Push(i.Image[i.ip+1])
i.ip += 2
i.Push(i.Image[i.PC+1])
i.PC += 2
case OpDup:
i.Push(i.data[i.sp])
i.ip++
i.PC++
case OpDrop:
i.sp--
i.ip++
i.PC++
case OpSwap:
i.data[i.sp], i.data[i.sp-1] = i.data[i.sp-1], i.data[i.sp]
i.ip++
i.PC++
case OpPush:
i.Rpush(i.Pop())
i.ip++
i.PC++
case OpPop:
i.Push(i.Rpop())
i.ip++
i.PC++
case OpLoop:
v := i.data[i.sp] - 1
if v > 0 {
i.data[i.sp] = v
i.ip = int(i.Image[i.ip+1])
i.PC = int(i.Image[i.PC+1])
} else {
i.sp--
i.ip += 2
i.PC += 2
}
case OpJump:
i.ip = int(i.Image[i.ip+1])
i.PC = int(i.Image[i.PC+1])
case OpReturn:
i.ip = int(i.Rpop() + 1)
i.PC = int(i.Rpop() + 1)
case OpGtJump:
if i.data[i.sp-1] > i.data[i.sp] {
i.ip = int(i.Image[i.ip+1])
i.PC = int(i.Image[i.PC+1])
} else {
i.ip += 2
i.PC += 2
}
i.sp -= 2
case OpLtJump:
if i.data[i.sp-1] < i.data[i.sp] {
i.ip = int(i.Image[i.ip+1])
i.PC = int(i.Image[i.PC+1])
} else {
i.ip += 2
i.PC += 2
}
i.sp -= 2
case OpNeJump:
if i.data[i.sp-1] != i.data[i.sp] {
i.ip = int(i.Image[i.ip+1])
i.PC = int(i.Image[i.PC+1])
} else {
i.ip += 2
i.PC += 2
}
i.sp -= 2
case OpEqJump:
if i.data[i.sp-1] == i.data[i.sp] {
i.ip = int(i.Image[i.ip+1])
i.PC = int(i.Image[i.PC+1])
} else {
i.ip += 2
i.PC += 2
}
i.sp -= 2
case OpFetch:
i.data[i.sp] = i.Image[i.data[i.sp]]
i.ip++
i.PC++
case OpStore:
i.Image[i.data[i.sp]] = i.data[i.sp-1]
i.sp -= 2
i.ip++
i.PC++
case OpAdd:
rhs := i.Pop()
i.data[i.sp] += rhs
i.ip++
i.PC++
case OpSub:
rhs := i.Pop()
i.data[i.sp] -= rhs
i.ip++
i.PC++
case OpMul:
rhs := i.Pop()
i.data[i.sp] *= rhs
i.ip++
i.PC++
case OpDimod:
lhs, rhs := i.data[i.sp-1], i.data[i.sp]
i.data[i.sp-1] = lhs % rhs
i.data[i.sp] = lhs / rhs
i.ip++
i.PC++
case OpAnd:
rhs := i.Pop()
i.data[i.sp] &= rhs
i.ip++
i.PC++
case OpOr:
rhs := i.Pop()
i.data[i.sp] |= rhs
i.ip++
i.PC++
case OpXor:
rhs := i.Pop()
i.data[i.sp] ^= rhs
i.ip++
i.PC++
case OpShl:
rhs := i.Pop()
i.data[i.sp] <<= uint8(rhs)
i.ip++
i.PC++
case OpShr:
rhs := i.Pop()
i.data[i.sp] >>= uint8(rhs)
i.ip++
i.PC++
case OpZeroExit:
if i.data[i.sp] == 0 {
i.ip = int(i.Rpop() + 1)
i.PC = int(i.Rpop() + 1)
i.sp--
} else {
i.ip++
i.PC++
}
case OpInc:
i.data[i.sp]++
i.ip++
i.PC++
case OpDec:
i.data[i.sp]--
i.ip++
i.PC++
case OpIn:
port := i.data[i.sp]
i.data[i.sp], i.ports[port] = i.ports[port], 0
i.ip++
i.PC++
case OpOut:
port := i.data[i.sp]
i.ports[port] = i.data[i.sp-1]
Expand All @@ -199,23 +197,20 @@ func (i *Instance) Run(toIP int) (ip int, err error) {
o.Flush()
}
}
i.ip++
i.PC++
case OpWait:
err = i.ioWait()
switch err.(type) {
case nil:
i.ip++
case breakError:
i.ip++
return i.ip, nil
i.PC++
default:
return i.ip, err
return err
}
default:
i.rsp++
i.address[i.rsp], i.ip = Cell(i.ip), int(op)
i.address[i.rsp], i.PC = Cell(i.PC), int(op)
}
i.insCount++
}
return i.ip, nil
return nil
}
18 changes: 11 additions & 7 deletions vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
// limitations under the License.

// Package vm blah.
// TODO: add
// complete file i/o
// - a reset func: clear stacks/reset ip to 0, accept Options (input / output may need to be reset as well)
// - a disasm func
// - discard the RuneWriter interface. Implement it by hand.
// TODO:
// - switch to raw stdio
// - complete file i/o
// - add a reset func: clear stacks/reset ip to 0, accept Options (input / output may need to be reset as well)
// - add a disasm func
// - implement communication with host go program via channels (in io)
// - go routines that leverage channels (watch out for the panic handler, we should have a global `done` channel)
// - BUG: I/O trashes ports in interactive mode. For example, the following returns 0 instead of the image size:
// -1 5 out 0 0 out wait 5 in putn
package vm

import "io"
Expand Down Expand Up @@ -113,7 +117,7 @@ func OptShrinkImage(shrink bool) Option {

// Instance represents an ngaro VM instance.
type Instance struct {
ip int
PC int
sp int
rsp int
Image Image
Expand All @@ -130,7 +134,7 @@ type Instance struct {
// New creates a new Ngaro Virtual Machine instance.
func New(image Image, imageFile string, opts ...Option) *Instance {
i := &Instance{
ip: 0,
PC: 0,
sp: -1,
rsp: -1,
Image: image,
Expand Down
Loading

0 comments on commit a028553

Please sign in to comment.