461 lines
7.5 KiB
Go
461 lines
7.5 KiB
Go
package asm
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"math"
|
|
// "strconv"
|
|
"strings"
|
|
|
|
"github.com/alecthomas/participle/v2"
|
|
"github.com/alecthomas/participle/v2/lexer"
|
|
|
|
"git.defalsify.org/festive/vm"
|
|
)
|
|
|
|
|
|
type Asm struct {
|
|
Instructions []*Instruction `@@*`
|
|
}
|
|
|
|
type Display struct {
|
|
Sym string `@Sym Whitespace`
|
|
//Val string `Quote (@Desc @Whitespace?)+ Quote Whitespace`
|
|
Val string `Quote (@Sym @Whitespace?)+ Quote Whitespace? EOL`
|
|
}
|
|
|
|
func(d Display) String() string {
|
|
return fmt.Sprintf("Display: %v %v", d.Sym, d.Val)
|
|
}
|
|
|
|
type Sig struct {
|
|
Sym string `@Sym Whitespace`
|
|
Size uint32 `@Size Whitespace`
|
|
Val uint8 `@Size Whitespace? EOL`
|
|
}
|
|
//
|
|
//func(s Sig) Capture(v []string) error {
|
|
// log.Printf("considering capture %v %v", v[0], len(v))
|
|
// if len(v) < 3 {
|
|
// return nil
|
|
// }
|
|
// s.Sym = v[0]
|
|
// r, err := strconv.Atoi(v[1])
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// s.Size = uint32(r)
|
|
// r, err = strconv.Atoi(v[2])
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// if r != 0 {
|
|
// r = 1
|
|
// }
|
|
// s.Val = uint8(r)
|
|
// log.Printf("after considering capture: %v", s)
|
|
// return nil
|
|
//}
|
|
|
|
func(s Sig) String() string {
|
|
return fmt.Sprintf("Sig: %v %v %v", s.Sym, s.Size, s.Val)
|
|
}
|
|
|
|
type Single struct {
|
|
One string `@Sym Whitespace? EOL`
|
|
}
|
|
|
|
func(s Single) String() string {
|
|
return fmt.Sprintf("Single: %v", s.One)
|
|
}
|
|
|
|
type Double struct {
|
|
One string `@Sym Whitespace`
|
|
Two string `@Sym Whitespace? EOL`
|
|
}
|
|
|
|
func(d Double) String() string {
|
|
return fmt.Sprintf("Double: %v %v", d.One, d.Two)
|
|
}
|
|
|
|
type Sized struct {
|
|
Sym string `@Sym Whitespace`
|
|
Size uint32 `@Size Whitespace? EOL`
|
|
}
|
|
|
|
func(s Sized) Capture(v []Sized) error {
|
|
log.Printf("foofofofofo")
|
|
return fmt.Errorf("foo foo foo")
|
|
}
|
|
|
|
func(s Sized) String() string {
|
|
return fmt.Sprintf("Sized: %v %v", s.Sym, s.Size)
|
|
}
|
|
|
|
type Arg struct {
|
|
ArgNone string "Discard?"
|
|
ArgDisplay *Display `@@?`
|
|
ArgSized *Sized `@@?`
|
|
ArgSig *Sig `@@?`
|
|
ArgDouble *Double `@@?`
|
|
ArgSingle *Single `@@?`
|
|
}
|
|
|
|
func (a Arg) String() string {
|
|
if a.ArgDisplay != nil {
|
|
return fmt.Sprintf("%s", a.ArgDisplay)
|
|
}
|
|
if a.ArgSig != nil {
|
|
return fmt.Sprintf("%s", a.ArgSig)
|
|
}
|
|
if a.ArgSized != nil {
|
|
return fmt.Sprintf("%s", a.ArgSized)
|
|
}
|
|
if a.ArgSingle != nil {
|
|
return fmt.Sprintf("%s", a.ArgSingle)
|
|
}
|
|
if a.ArgDouble != nil {
|
|
return fmt.Sprintf("%s", a.ArgDouble)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
type Instruction struct {
|
|
OpCode string `@Ident`
|
|
OpArg Arg `@@`
|
|
Comment string `Comment?`
|
|
}
|
|
|
|
func (i Instruction) String() string {
|
|
return fmt.Sprintf("%s %s", i.OpCode, i.OpArg)
|
|
}
|
|
|
|
var (
|
|
asmLexer = lexer.MustSimple([]lexer.SimpleRule{
|
|
{"Comment", `(?:#)[^\n]*\n?`},
|
|
{"Ident", `^[A-Z]+`},
|
|
{"SizeSig", `[0-9]+\s+{?:[0-9]}`},
|
|
{"Size", `[0-9]+`},
|
|
{"Sym", `[a-zA-Z_][a-zA-Z0-9_]+`},
|
|
{"Whitespace", `[ \t]+`},
|
|
{"Discard", `^\s+[\n\r]+$`},
|
|
{"EOL", `[\n\r]+`},
|
|
{"Quote", `["']`},
|
|
})
|
|
asmParser = participle.MustBuild[Asm](
|
|
participle.Lexer(asmLexer),
|
|
participle.Elide("Comment", "Whitespace"),
|
|
)
|
|
)
|
|
|
|
func numSize(n uint32) int {
|
|
v := math.Log2(float64(n))
|
|
return int(((v - 1) / 8) + 1)
|
|
}
|
|
|
|
func writeOpcode(op vm.Opcode, w *bytes.Buffer) (int, error) {
|
|
bn := [2]byte{}
|
|
binary.BigEndian.PutUint16(bn[:], uint16(op))
|
|
n, err := w.Write(bn[:])
|
|
return n, err
|
|
}
|
|
|
|
func writeSym(s string, w *bytes.Buffer) (int, error) {
|
|
sz := len(s)
|
|
if sz > 255 {
|
|
return 0, fmt.Errorf("string size %v too big", sz)
|
|
}
|
|
w.Write([]byte{byte(sz)})
|
|
return w.WriteString(s)
|
|
}
|
|
|
|
func writeDisplay(s string, w *bytes.Buffer) (int, error) {
|
|
s = strings.Trim(s, "\"'")
|
|
sz := len(s)
|
|
if sz > 255 {
|
|
return 0, fmt.Errorf("string size %v too big", sz)
|
|
}
|
|
w.Write([]byte{byte(sz)})
|
|
return w.WriteString(s)
|
|
}
|
|
|
|
func writeSize(n uint32, w *bytes.Buffer) (int, error) {
|
|
bn := [4]byte{}
|
|
sz := numSize(n)
|
|
if sz > 4 {
|
|
return 0, fmt.Errorf("number size %v too big", sz)
|
|
}
|
|
w.Write([]byte{byte(sz)})
|
|
binary.BigEndian.PutUint32(bn[:], n)
|
|
c := 4-sz
|
|
return w.Write(bn[c:])
|
|
}
|
|
|
|
func parseSingle(op vm.Opcode, arg Arg, w io.Writer) (int, error) {
|
|
var rn int
|
|
|
|
v := arg.ArgSingle
|
|
if v == nil {
|
|
return 0, nil
|
|
}
|
|
|
|
b := bytes.NewBuffer(nil)
|
|
|
|
n, err := writeOpcode(op, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
n, err = writeSym(v.One, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
if w != nil {
|
|
rn, err = w.Write(b.Bytes())
|
|
} else {
|
|
rn = 0
|
|
}
|
|
return rn, err
|
|
}
|
|
|
|
func parseDisplay(op vm.Opcode, arg Arg, w io.Writer) (int, error) {
|
|
var rn int
|
|
|
|
v := arg.ArgDisplay
|
|
if v == nil {
|
|
return 0, nil
|
|
}
|
|
|
|
b := bytes.NewBuffer(nil)
|
|
|
|
n, err := writeOpcode(op, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
n, err = writeSym(v.Sym, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
n, err = writeDisplay(v.Val, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
if w != nil {
|
|
rn, err = w.Write(b.Bytes())
|
|
} else {
|
|
rn = 0
|
|
}
|
|
return rn, err
|
|
}
|
|
|
|
func parseDouble(op vm.Opcode, arg Arg, w io.Writer) (int, error) {
|
|
var rn int
|
|
|
|
v := arg.ArgDouble
|
|
if v == nil {
|
|
return 0, nil
|
|
}
|
|
|
|
b := bytes.NewBuffer(nil)
|
|
|
|
n, err := writeOpcode(op, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
n, err = writeSym(v.One, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
n, err = writeSym(v.Two, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
if w != nil {
|
|
rn, err = w.Write(b.Bytes())
|
|
} else {
|
|
rn = 0
|
|
}
|
|
return rn, err
|
|
}
|
|
|
|
func parseSized(op vm.Opcode, arg Arg, w io.Writer) (int, error) {
|
|
var rn int
|
|
|
|
v := arg.ArgSized
|
|
if v == nil {
|
|
return 0, nil
|
|
}
|
|
|
|
b := bytes.NewBuffer(nil)
|
|
|
|
n, err := writeOpcode(op, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
n, err = writeSym(v.Sym, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
n, err = writeSize(v.Size, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
if w != nil {
|
|
rn, err = w.Write(b.Bytes())
|
|
} else {
|
|
rn = 0
|
|
}
|
|
return rn, err
|
|
}
|
|
|
|
func parseSig(op vm.Opcode, arg Arg, w io.Writer) (int, error) {
|
|
var rn int
|
|
|
|
v := arg.ArgSig
|
|
if v == nil {
|
|
return 0, nil
|
|
}
|
|
|
|
b := bytes.NewBuffer(nil)
|
|
|
|
n, err := writeOpcode(op, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
n, err = writeSym(v.Sym, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
n, err = writeSize(v.Size, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
if v.Val == 0 {
|
|
n, err = b.Write([]byte{0x00})
|
|
} else {
|
|
n, err = b.Write([]byte{0x01})
|
|
}
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
|
|
|
|
if w != nil {
|
|
rn, err = w.Write(b.Bytes())
|
|
} else {
|
|
rn = 0
|
|
}
|
|
return rn, err
|
|
}
|
|
|
|
func parseNoarg(op vm.Opcode, arg Arg, w io.Writer) (int, error) {
|
|
var rn int
|
|
|
|
b := bytes.NewBuffer(nil)
|
|
|
|
n, err := writeOpcode(op, b)
|
|
rn += n
|
|
if err != nil {
|
|
return rn, err
|
|
}
|
|
if w != nil {
|
|
rn, err = w.Write(b.Bytes())
|
|
} else {
|
|
rn = 0
|
|
}
|
|
return rn, err
|
|
}
|
|
|
|
func Parse(s string, w io.Writer) (int, error) {
|
|
rd := strings.NewReader(s)
|
|
ast, err := asmParser.Parse("file", rd)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var rn int
|
|
for _, v := range ast.Instructions {
|
|
log.Printf("parsing line %v: %v", v.OpCode, v.OpArg)
|
|
op := vm.OpcodeIndex[v.OpCode]
|
|
n, err := parseSized(op, v.OpArg, w)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
if n > 0 {
|
|
rn += n
|
|
continue
|
|
}
|
|
n, err = parseDisplay(op, v.OpArg, w)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
if n > 0 {
|
|
rn += n
|
|
continue
|
|
}
|
|
n, err = parseSig(op, v.OpArg, w)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
if n > 0 {
|
|
rn += n
|
|
continue
|
|
}
|
|
n, err = parseDouble(op, v.OpArg, w)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
if n > 0 {
|
|
rn += n
|
|
continue
|
|
}
|
|
n, err = parseSingle(op, v.OpArg, w)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
if n > 0 {
|
|
rn += n
|
|
continue
|
|
}
|
|
n, err = parseNoarg(op, v.OpArg, w)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
if n > 0 {
|
|
rn += n
|
|
continue
|
|
}
|
|
|
|
}
|
|
return rn, err
|
|
}
|