From ac4a2bac00e4d794349e6af922fa0f7bcf4c47f2 Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 2 Apr 2023 15:00:56 +0100 Subject: [PATCH] Add disasembler-ish - bytecode to instruction debug output --- README.md | 9 ++++ go/dev/disasm/main.go | 27 ++++++++++ go/vm/debug.go | 93 ++++++++++++++++++++++++++++++++++ go/vm/debug_test.go | 114 ++++++++++++++++++++++++++++++++++++++++++ go/vm/opcodes.go | 14 ++++++ go/vm/runner.go | 4 -- go/vm/vm.go | 2 +- 7 files changed, 258 insertions(+), 5 deletions(-) create mode 100644 go/dev/disasm/main.go create mode 100644 go/vm/debug.go create mode 100644 go/vm/debug_test.go diff --git a/README.md b/README.md index 0ee3724..ddf8c2b 100644 --- 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/ ` + +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 new file mode 100644 index 0000000..c5f8954 --- /dev/null +++ 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 new file mode 100644 index 0000000..d74533e --- /dev/null +++ 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 new file mode 100644 index 0000000..1e1ba65 --- /dev/null +++ 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 index d375e88..b7ec627 100644 --- 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 index 9e3185f..ca03cf0 100644 --- 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 index db00c6b..60db316 100644 --- 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