Skip to content

Commit

Permalink
all: remove Image type
Browse files Browse the repository at this point in the history
Fixes #3
  • Loading branch information
db47h committed Aug 17, 2016
1 parent 7f674f1 commit 388372e
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 73 deletions.
2 changes: 1 addition & 1 deletion asm/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ var opcodes = [...][]string{
}

// Assemble compiles assembly read from the supplied io.Reader and returns the
// resulting image and error if any.
// resulting memory image and error if any.
//
// Then name parameter is used only in error messages to name the source of the
// error. If the io.Reader is a file, name should be the file name.
Expand Down
8 changes: 4 additions & 4 deletions asm/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func (p *parser) abort() bool { return len(p.errs) >= maxErrors }

// write is the actual compilation function. It emits the given value at the
// current compile address, then imcrements it. It also takes care of managing
// the image size.
// the memory image size.
func (p *parser) write(v vm.Cell) {
for p.pc >= len(p.i) {
p.i = append(p.i, make([]vm.Cell, 16384)...)
Expand Down Expand Up @@ -231,9 +231,9 @@ func (p *parser) scan() (tok rune, s string, v int) {
return tok, s, v
}

// Parse does the parsing and compiling. Returns the compiled VM image as a Cell
// slice and any error that occurred. If not nil, the returned error can safely
// be cast to an ErrAsm value that will contain up to 10 entries.
// Parse does the parsing and compiling. Returns the compiled VM memory image as
// a Cell slice and any error that occurred. If not nil, the returned error can
// safely be cast to an ErrAsm value that will contain up to 10 entries.
func (p *parser) Parse(name string, r io.Reader) ([]vm.Cell, error) {
// state:
// 0: accept anything
Expand Down
4 changes: 2 additions & 2 deletions cmd/retro/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ func dumpSlice(w *ngi.ErrWriter, a []vm.Cell) error {
return w.Err
}

// Dump dumps the virtual machine stacks and image to the specified io.Writer.
// Dump dumps the virtual machine stacks and memory image to the specified io.Writer.
func dumpVM(i *vm.Instance, size int, w io.Writer) error {
ew := ngi.NewErrWriter(w)
ew.Write([]byte{'\x1C'})
dumpSlice(ew, i.Data())
ew.Write([]byte{'\x1D'})
dumpSlice(ew, i.Address())
ew.Write([]byte{'\x1D'})
return dumpSlice(ew, i.Image[:size])
return dumpSlice(ew, i.Mem[:size])
}
21 changes: 18 additions & 3 deletions cmd/retro/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ func port2Handler(w io.Writer) func(i *vm.Instance, v, port vm.Cell) error {
}
}

func shrinkSave(mem []vm.Cell, fileName string) error {
end := vm.Cell(len(mem))
if len(mem) < 4 {
return nil
}
if here := mem[3]; *shrink && here >= 0 && here < end {
end = here
}
err := vm.Save(mem[:end], *outFileName)
if err != nil {
return err
}
return nil
}

func setupIO() (raw bool, tearDown func()) {
var err error
if *rawIO {
Expand All @@ -85,8 +100,8 @@ func atExit(i *vm.Instance, err error) {
}
fmt.Fprintf(os.Stderr, "\n%+v\n", err)
if i != nil {
if i.PC < len(i.Image) {
fmt.Fprintf(os.Stderr, "PC: %v (%v), Stack: %v, Addr: %v\n", i.PC, i.Image[i.PC], i.Data(), i.Address())
if i.PC < len(i.Mem) {
fmt.Fprintf(os.Stderr, "PC: %v (%v), Stack: %v, Addr: %v\n", i.PC, i.Mem[i.PC], i.Data(), i.Address())
} else {
fmt.Fprintf(os.Stderr, "PC: %v, Stack: %v\nAddr: %v\n", i.PC, i.Data(), i.Address())
}
Expand Down Expand Up @@ -118,7 +133,7 @@ func main() {

// default options
var opts = []vm.Option{
vm.Shrink(*shrink),
vm.SaveMemImage(shrinkSave),
vm.Output(output),
}

Expand Down
26 changes: 13 additions & 13 deletions vm/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,20 +145,20 @@ func (i *Instance) Run() (err error) {
switch e := e.(type) {
case error:
err = errors.Wrapf(e, "Recovered error @pc=%d/%d, stack %d/%d, rstack %d/%d",
i.PC, len(i.Image), i.sp, len(i.data)-1, i.rsp, len(i.address)-1)
i.PC, len(i.Mem), i.sp, len(i.data)-1, i.rsp, len(i.address)-1)
default:
panic(e)
}
}
}()
i.insCount = 0
for i.PC < len(i.Image) {
op := i.Image[i.PC]
for i.PC < len(i.Mem) {
op := i.Mem[i.PC]
switch op {
case OpNop:
i.PC++
case OpLit:
i.Push(i.Image[i.PC+1])
i.Push(i.Mem[i.PC+1])
i.PC += 2
case OpDup:
i.sp++
Expand All @@ -180,48 +180,48 @@ func (i *Instance) Run() (err error) {
v := i.Tos - 1
if v > 0 {
i.Tos = v
i.PC = int(i.Image[i.PC+1])
i.PC = int(i.Mem[i.PC+1])
} else {
i.Drop()
i.PC += 2
}
case OpJump:
i.PC = int(i.Image[i.PC+1])
i.PC = int(i.Mem[i.PC+1])
case OpReturn:
i.PC = int(i.Rpop() + 1)
case OpGtJump:
if i.data[i.sp] > i.Tos {
i.PC = int(i.Image[i.PC+1])
i.PC = int(i.Mem[i.PC+1])
} else {
i.PC += 2
}
i.Drop2()
case OpLtJump:
if i.data[i.sp] < i.Tos {
i.PC = int(i.Image[i.PC+1])
i.PC = int(i.Mem[i.PC+1])
} else {
i.PC += 2
}
i.Drop2()
case OpNeJump:
if i.data[i.sp] != i.Tos {
i.PC = int(i.Image[i.PC+1])
i.PC = int(i.Mem[i.PC+1])
} else {
i.PC += 2
}
i.Drop2()
case OpEqJump:
if i.data[i.sp] == i.Tos {
i.PC = int(i.Image[i.PC+1])
i.PC = int(i.Mem[i.PC+1])
} else {
i.PC += 2
}
i.Drop2()
case OpFetch:
i.Tos = i.Image[i.Tos]
i.Tos = i.Mem[i.Tos]
i.PC++
case OpStore:
i.Image[i.Tos] = i.data[i.sp]
i.Mem[i.Tos] = i.data[i.sp]
i.Drop2()
i.PC++
case OpAdd:
Expand Down Expand Up @@ -317,7 +317,7 @@ func (i *Instance) Run() (err error) {
i.rsp++
i.address[i.rsp] = i.rtos
i.rtos, i.PC = Cell(i.PC), int(op)
for i.PC < len(i.Image) && i.Image[i.PC] == OpNop {
for i.PC < len(i.Mem) && i.Mem[i.PC] == OpNop {
i.PC++
}
} else if i.opHandler != nil {
Expand Down
4 changes: 2 additions & 2 deletions vm/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func runAsmImage(assembly, name string, opts ...vm.Option) (*vm.Instance, error)
}

func setup(code, stack, rstack C) *vm.Instance {
i, err := vm.New(vm.Image(code), "")
i, err := vm.New([]vm.Cell(code), "")
if err != nil {
panic(err)
}
Expand All @@ -75,7 +75,7 @@ func check(t *testing.T, testName string, i *vm.Instance, ip int, stack C, rstac
return false
}
if ip <= 0 {
ip = len(i.Image)
ip = len(i.Mem)
}
if ip != i.PC {
t.Errorf("%v", fmt.Errorf("%s: Bad IP %d != %d", testName, i.PC, ip))
Expand Down
6 changes: 5 additions & 1 deletion vm/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
// VM, please refer to
// http://retroforth.org/docs/The_Ngaro_Virtual_Machine.html.
//
// Another goal is to separate the VM core from its traditional use in
// association with the Retro language. Some Retro specific behaviors have been
// moved from the VM to the retro command line tool. See https://github.com/db47h/ngaro/cmd/retro.
//
// Custom opcodes are implemented by intercepting implicit calls to negative
// memory addresses. This limits the total addressable memory to 2GiB on 32 bits
// systems, but this also allows the VM to be fully backwards compatible with
Expand Down Expand Up @@ -60,5 +64,5 @@
//
// -1 5 io putn
//
// should give you the size of the image.
// should give you the total memory size.
package vm
17 changes: 10 additions & 7 deletions vm/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,20 @@ func (i *Instance) Wait(v, port Cell) error {
var b [1]byte
switch v {
case 1: // save image
i.Image.Save(i.imageFile, i.shrink)
err := i.memDump(i.Mem, i.imageFile)
if err != nil {
return err
}
i.WaitReply(0, 4)
case 2: // include file
i.WaitReply(0, 4)
f, err := os.Open(i.Image.DecodeString(i.Pop()))
f, err := os.Open(DecodeString(i.Mem, i.Pop()))
if err != nil {
return err
}
i.PushInput(f)
case -1: // open file
fd := i.openfile(i.Image.DecodeString(i.data[i.sp]), i.Tos)
fd := i.openfile(DecodeString(i.Mem, i.data[i.sp]), i.Tos)
i.Drop2()
i.WaitReply(fd, 4)
case -2: // read byte
Expand Down Expand Up @@ -224,7 +227,7 @@ func (i *Instance) Wait(v, port Cell) error {
}
i.WaitReply(sz, 4)
case -8: // delete
err := os.Remove(i.Image.DecodeString(i.Pop()))
err := os.Remove(DecodeString(i.Mem, i.Pop()))
if err != nil {
i.WaitReply(0, 4)
} else {
Expand All @@ -239,7 +242,7 @@ func (i *Instance) Wait(v, port Cell) error {
switch i.Ports[5] {
case -1:
// image size
i.Ports[5] = Cell(len(i.Image))
i.Ports[5] = Cell(len(i.Mem))
// -2, -3, -4: canvas related
case -5:
// data depth
Expand All @@ -254,12 +257,12 @@ func (i *Instance) Wait(v, port Cell) error {
case -9:
// exit VM
i.Ports[5] = 0
i.PC = len(i.Image) - 1 // will be incremented when returning
i.PC = len(i.Mem) - 1 // will be incremented when returning
case -10:
// environment query
src, dst := i.Tos, i.data[i.sp]
i.Drop2()
i.Image.EncodeString(dst, os.Getenv(i.Image.DecodeString(src)))
EncodeString(i.Mem, dst, os.Getenv(DecodeString(i.Mem, src)))
i.Ports[5] = 0
case -11:
// console width
Expand Down
51 changes: 21 additions & 30 deletions vm/image.go → vm/mem.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@ import (
"unsafe"
)

// Image encapsulates a VM's memory
type Image []Cell

// Load loads an image from file fileName. Returns a VM Image ready to run, the
// actual number of cells read from the file and any error.
// Load loads a memory image from file fileName. Returns a VM Cell slice ready to run
// from, the actual number of cells read from the file and any error.
//
// The returned Image should have its length equal equal to the maximum of the
// The returned slice should have its length equal to the maximum of the
// requested minimum size and the image file size + 1024 free cells.
func Load(fileName string, minSize int) (i Image, fileCells int, err error) {
func Load(fileName string, minSize int) (mem []Cell, fileCells int, err error) {
f, err := os.Open(fileName)
if err != nil {
return nil, 0, err
Expand All @@ -51,52 +48,46 @@ func Load(fileName string, minSize int) (i Image, fileCells int, err error) {
if minSize > imgCells {
imgCells = minSize
}
i = make(Image, imgCells)
err = binary.Read(f, binary.LittleEndian, i[:fileCells])
mem = make([]Cell, imgCells)
err = binary.Read(f, binary.LittleEndian, mem[:fileCells])
if err != nil {
return nil, 0, err
}
return i, fileCells, nil
return mem, fileCells, nil
}

// Save saves the image. If the shrink parameter is true, only the portion of
// the image from offset 0 to HERE will be saved. Note that HERE is read from
// memory cell 3.
func (i Image) Save(fileName string, shrink bool) error {
// Save saves a Cell slice to an memory image file.
func Save(mem []Cell, fileName string) error {
f, err := os.Create(fileName)
if err != nil {
return err
}
defer f.Close()

if shrink {
i = i[0:i[3]]
}
return binary.Write(f, binary.LittleEndian, i)
return binary.Write(f, binary.LittleEndian, mem)
}

// DecodeString returns the string starting at position start in the image.
// Strings stored in the image must be zero terminated. The trailing '\0' is
// not returned.
func (i Image) DecodeString(start Cell) string {
// DecodeString returns the string starting at position start in the specified
// slice. Strings stored in the slice must be zero terminated. The trailing '\0'
// is not returned.
func DecodeString(mem []Cell, start Cell) string {
pos := int(start)
end := pos
for ; end < len(i) && i[end] != 0; end++ {
for ; end < len(mem) && mem[end] != 0; end++ {
}
str := make([]byte, end-pos)
for idx, c := range i[pos:end] {
for idx, c := range mem[pos:end] {
str[idx] = byte(c)
}
return string(str)
}

// EncodeString writes the given string at position start in the Image and
// terminates it with a '\0' Cell.
func (i Image) EncodeString(start Cell, s string) {
// EncodeString writes the given string at position start in specified slice
// and terminates it with a '\0' Cell.
func EncodeString(mem []Cell, start Cell, s string) {
pos := int(start)
for _, c := range []byte(s) {
i[pos] = Cell(c)
mem[pos] = Cell(c)
pos++
}
i[pos] = 0
mem[pos] = 0
}
Loading

0 comments on commit 388372e

Please sign in to comment.