Working disassembler
This commit is contained in:
@@ -1,39 +1,91 @@
|
|||||||
package asm
|
package asm
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
import "math"
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
import "github.com/firestuff/subcoding/vm"
|
import "github.com/firestuff/subcoding/vm"
|
||||||
import "github.com/pkg/errors"
|
import "github.com/pkg/errors"
|
||||||
|
|
||||||
func Disassemble(prog *vm.Program) ([]byte, error) {
|
// Lots of manual yaml assembly because the yaml package is missing features (flow in nested arrays)
|
||||||
|
|
||||||
|
func Disassemble(prog *vm.Program) (string, error) {
|
||||||
|
fncs := []string{}
|
||||||
|
|
||||||
for f, fnc := range prog.Functions {
|
for f, fnc := range prog.Functions {
|
||||||
_, err := disassembleFunction(fnc)
|
dis, err := disassembleFunction(fnc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "At function index %d", f)
|
return "", errors.Wrapf(err, "At function index %d", f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fncs = append(fncs, dis)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return fmt.Sprintf("functions:\n%s", strings.Join(fncs, "")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func disassembleFunction(fnc *vm.Function) ([]byte, error) {
|
func disassembleFunction(fnc *vm.Function) (string, error) {
|
||||||
|
instrs := []string{}
|
||||||
|
|
||||||
for i, instr := range fnc.Instructions {
|
for i, instr := range fnc.Instructions {
|
||||||
_, err := disassembleInstruction(instr)
|
dis, err := disassembleInstruction(instr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "At instruction index %d", i)
|
return "", errors.Wrapf(err, "At instruction index %d", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instrs = append(instrs, dis)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return fmt.Sprintf("- - %s\n", strings.Join(instrs, "\n - ")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func disassembleInstruction(in *vm.Instruction) ([]byte, error) {
|
func disassembleInstruction(in *vm.Instruction) (string, error) {
|
||||||
parts := []string{
|
parts := []string{
|
||||||
nameByOpCode[in.OpCode],
|
nameByOpCode[in.OpCode],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i, t := range operandsByOpCode[in.OpCode] {
|
||||||
|
op, err := disassembleOperand(in.Operands[i], t)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = append(parts, op)
|
||||||
|
}
|
||||||
|
|
||||||
encoded := fmt.Sprintf("[%s]", strings.Join(parts, ", "))
|
encoded := fmt.Sprintf("[%s]", strings.Join(parts, ", "))
|
||||||
|
|
||||||
return []byte(encoded), nil
|
return encoded, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func disassembleOperand(op *vm.Operand, t operandType) (string, error) {
|
||||||
|
switch op.Type {
|
||||||
|
case vm.GlobalMemoryIndex:
|
||||||
|
return fmt.Sprintf("g%d", op.Value), nil
|
||||||
|
|
||||||
|
case vm.FunctionMemoryIndex:
|
||||||
|
return fmt.Sprintf("f%d", op.Value), nil
|
||||||
|
|
||||||
|
case vm.Literal:
|
||||||
|
switch t {
|
||||||
|
case u:
|
||||||
|
return fmt.Sprintf("%d", op.Value), nil
|
||||||
|
|
||||||
|
case s:
|
||||||
|
return fmt.Sprintf("%+d", int64(op.Value)), nil
|
||||||
|
|
||||||
|
case us:
|
||||||
|
// Take our best guess
|
||||||
|
if op.Value > math.MaxInt64 {
|
||||||
|
return fmt.Sprintf("%+d", int64(op.Value)), nil
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%d", op.Value), nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Invalid operand assembler type: %d", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Invalid operand type: %d", op.Type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,27 @@ import "testing"
|
|||||||
import "github.com/firestuff/subcoding/asm"
|
import "github.com/firestuff/subcoding/asm"
|
||||||
|
|
||||||
func TestRoundTrip(t *testing.T) {
|
func TestRoundTrip(t *testing.T) {
|
||||||
prog, err := asm.AssembleString(`
|
src := `functions:
|
||||||
functions:
|
|
||||||
- - [nop]
|
- - [nop]
|
||||||
`)
|
- [mov, g0, 1]
|
||||||
|
- [add, f0, 5]
|
||||||
|
- [mov, g1, -5]
|
||||||
|
- [eq, 2, f0]
|
||||||
|
- [jmpt, -3]
|
||||||
|
- - [add, g0, -1]
|
||||||
|
- [cal, +5]
|
||||||
|
`
|
||||||
|
prog, err := asm.AssembleString(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = asm.Disassemble(prog)
|
dis, err := asm.Disassemble(prog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dis != src {
|
||||||
|
t.Fatalf("Disassembly mismatch: expected=%v actual=%v", src, dis)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user