commit ac4a2bac00e4d794349e6af922fa0f7bcf4c47f2
parent aec0564ceae11caee70e02393a093489099f7c0f
Author: lash <dev@holbrook.no>
Date: Sun, 2 Apr 2023 15:00:56 +0100
Add disasembler-ish - bytecode to instruction debug output
Diffstat:
7 files changed, 258 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
@@ -163,6 +163,15 @@ If `data_directory` is not set, current directory will be used.
if `root_symbol` is not set, the symbol `root` will be used.
+### Disassembler
+
+`go run ./dev/testdata/ <binary_file>`
+
+The output from this tool is to be considered debugging output, as the assembly language isn't formalized yet.
+
+In the meantime, it will at least list all the instructions, and thus validate the file.
+
+
### Assembler
**TBD**
diff --git a/go/dev/disasm/main.go b/go/dev/disasm/main.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "io/ioutil"
+
+ "git.defalsify.org/festive/vm"
+)
+
+func main() {
+ if (len(os.Args) < 2) {
+ os.Exit(1)
+ }
+ fp := os.Args[1]
+ v, err := ioutil.ReadFile(fp)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "read error: %v", err)
+ os.Exit(1)
+ }
+ r, err := vm.ToString(v)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "parse error: %v", err)
+ os.Exit(1)
+ }
+ fmt.Printf(r)
+}
diff --git a/go/vm/debug.go b/go/vm/debug.go
@@ -0,0 +1,93 @@
+package vm
+
+import (
+ "fmt"
+)
+
+
+func ToString(b []byte) (string, error) {
+ var s string
+ running := true
+ for running {
+ op, bb, err := opSplit(b)
+ b = bb
+ if err != nil {
+ return "", err
+ }
+ opString := OpcodeString[op]
+ if opString == "" {
+ return "", fmt.Errorf("unknown opcode: %v", op)
+ }
+ s += opString
+
+ switch op {
+ case CATCH:
+ r, n, m, bb, err := ParseCatch(b)
+ b = bb
+ if err != nil {
+ return "", err
+ }
+ vv := 0
+ if m {
+ vv = 1
+ }
+ s = fmt.Sprintf("%s %s %v %v # invertmatch=%v", s, r, n, vv, m)
+ case CROAK:
+ n, m, bb, err := ParseCroak(b)
+ b = bb
+ if err != nil {
+ return "", err
+ }
+ vv := 0
+ if m {
+ vv = 1
+ }
+ s = fmt.Sprintf("%s %v %v # invertmatch=%v", s, n, vv, m)
+ case LOAD:
+ r, n, bb, err := ParseLoad(b)
+ b = bb
+ if err != nil {
+ return "", err
+ }
+ s = fmt.Sprintf("%s %s %v", s, r, n)
+ case RELOAD:
+ r, bb, err := ParseReload(b)
+ b = bb
+ if err != nil {
+ return "", err
+ }
+ s = fmt.Sprintf("%s %s", s, r)
+ case MAP:
+ r, bb, err := ParseMap(b)
+ b = bb
+ if err != nil {
+ return "", err
+ }
+ s = fmt.Sprintf("%s %s", s, r)
+ case MOVE:
+ r, bb, err := ParseMove(b)
+ b = bb
+ if err != nil {
+ return "", err
+ }
+ s = fmt.Sprintf("%s %s", s, r)
+ case INCMP:
+ r, v, bb, err := ParseInCmp(b)
+ b = bb
+ if err != nil {
+ return "", err
+ }
+ s = fmt.Sprintf("%s %s %s", s, r, v)
+ case HALT:
+ b, err = ParseHalt(b)
+ if err != nil {
+ return "", err
+ }
+ }
+ s += "\n"
+ if len(b) == 0 {
+ running = false
+ }
+ }
+ return s, nil
+}
diff --git a/go/vm/debug_test.go b/go/vm/debug_test.go
@@ -0,0 +1,114 @@
+package vm
+
+import (
+ "testing"
+)
+
+
+func TestToString(t *testing.T) {
+ var b []byte
+ var r string
+ var expect string
+ var err error
+
+ b = NewLine(nil, CATCH, []string{"xyzzy"}, []byte{0x0d}, []uint8{1})
+ r, err = ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = "CATCH xyzzy 13 1 # invertmatch=true\n"
+ if r != expect {
+ t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
+ }
+
+ b = NewLine(nil, CROAK, nil, []byte{0x0d}, []uint8{1})
+ r, err = ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = "CROAK 13 1 # invertmatch=true\n"
+ if r != expect {
+ t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
+ }
+
+ b = NewLine(nil, LOAD, []string{"foo"}, []byte{0x0a}, nil)
+ r, err = ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = "LOAD foo 10\n"
+ if r != expect {
+ t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
+ }
+
+ b = NewLine(nil, RELOAD, []string{"bar"}, nil, nil)
+ r, err = ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = "RELOAD bar\n"
+ if r != expect {
+ t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
+ }
+
+ b = NewLine(nil, MAP, []string{"inky_pinky"}, nil, nil)
+ r, err = ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = "MAP inky_pinky\n"
+ if r != expect {
+ t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
+ }
+
+ b = NewLine(nil, MOVE, []string{"blinky_clyde"}, nil, nil)
+ r, err = ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = "MOVE blinky_clyde\n"
+ if r != expect {
+ t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
+ }
+
+ b = NewLine(nil, HALT, nil, nil, nil)
+ r, err = ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = "HALT\n"
+ if r != expect {
+ t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
+ }
+
+ b = NewLine(nil, INCMP, []string{"13", "baz"}, nil, nil)
+ r, err = ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = "INCMP 13 baz\n"
+ if r != expect {
+ t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
+ }
+}
+
+func TestToStringMultiple(t *testing.T) {
+ b := NewLine(nil, INCMP, []string{"1", "foo"}, nil, nil)
+ b = NewLine(b, INCMP, []string{"2", "bar"}, nil, nil)
+ b = NewLine(b, CATCH, []string{"aiee"}, []byte{0x02, 0x9a}, []uint8{0})
+ b = NewLine(b, LOAD, []string{"inky"}, []byte{0x2a}, nil)
+ b = NewLine(b, HALT, nil, nil, nil)
+ r, err := ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect := `INCMP 1 foo
+INCMP 2 bar
+CATCH aiee 666 0 # invertmatch=false
+LOAD inky 42
+HALT
+`
+ if r != expect {
+ t.Fatalf("expected:\n\t%v\ngot:\n\t%v", expect, r)
+ }
+}
diff --git a/go/vm/opcodes.go b/go/vm/opcodes.go
@@ -17,3 +17,17 @@ const (
INCMP = 8
_MAX = 8
)
+
+var (
+ OpcodeString = map[Opcode]string{
+ NOOP: "NOOP",
+ CATCH: "CATCH",
+ CROAK: "CROAK",
+ LOAD: "LOAD",
+ RELOAD: "RELOAD",
+ MAP: "MAP",
+ MOVE: "MOVE",
+ HALT: "HALT",
+ INCMP: "INCMP",
+ }
+)
diff --git a/go/vm/runner.go b/go/vm/runner.go
@@ -19,13 +19,11 @@ import (
func Run(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
running := true
for running {
- log.Printf("code before 0x%x", b)
op, bb, err := opSplit(b)
if err != nil {
return b, err
}
b = bb
- log.Printf("code after 0x%x", b)
switch op {
case CATCH:
b, err = RunCatch(b, st, rs, ctx)
@@ -41,7 +39,6 @@ func Run(b []byte, st *state.State, rs resource.Resource, ctx context.Context) (
b, err = RunMove(b, st, rs, ctx)
case INCMP:
b, err = RunInCmp(b, st, rs, ctx)
- log.Printf("bb %v", b)
case HALT:
b, err = RunHalt(b, st, rs, ctx)
return b, err
@@ -51,7 +48,6 @@ func Run(b []byte, st *state.State, rs resource.Resource, ctx context.Context) (
if err != nil {
return b, err
}
- log.Printf("aa %v", b)
if len(b) == 0 {
return []byte{}, nil
}
diff --git a/go/vm/vm.go b/go/vm/vm.go
@@ -7,7 +7,7 @@ import (
"git.defalsify.org/festive/state"
)
-func Parse(b []byte) (Opcode, []byte, error) {
+func ParseOp(b []byte) (Opcode, []byte, error) {
op, b, err := opSplit(b)
if err != nil {
return NOOP, b, err