vise/go/asm/asm.go

388 lines
6.2 KiB
Go
Raw Normal View History

2023-04-04 11:32:39 +02:00
package asm
import (
2023-04-04 21:32:40 +02:00
"bytes"
"encoding/binary"
2023-04-04 11:32:39 +02:00
"fmt"
"io"
2023-04-05 09:59:24 +02:00
"log"
2023-04-04 21:32:40 +02:00
"math"
2023-04-04 11:32:39 +02:00
"strings"
"github.com/alecthomas/participle/v2"
"github.com/alecthomas/participle/v2/lexer"
2023-04-04 11:38:01 +02:00
"git.defalsify.org/festive/vm"
2023-04-04 11:32:39 +02:00
)
2023-04-04 11:38:01 +02:00
2023-04-04 11:32:39 +02:00
type Asm struct {
Instructions []*Instruction `@@*`
}
type Display struct {
Sym string `@Sym Whitespace`
2023-04-05 12:22:13 +02:00
Val string `Quote (@Sym @Whitespace?)+ Quote`
2023-04-04 11:32:39 +02:00
}
func(d Display) String() string {
return fmt.Sprintf("Display: %v %v", d.Sym, d.Val)
}
type Single struct {
2023-04-05 12:22:13 +02:00
One string `@Sym`
2023-04-04 11:32:39 +02:00
}
func(s Single) String() string {
return fmt.Sprintf("Single: %v", s.One)
}
type Double struct {
One string `@Sym Whitespace`
2023-04-05 12:22:13 +02:00
Two string `@Sym`
2023-04-04 11:32:39 +02:00
}
func(d Double) String() string {
return fmt.Sprintf("Double: %v %v", d.One, d.Two)
}
type Sized struct {
Sym string `@Sym Whitespace`
2023-04-05 12:22:13 +02:00
Size uint32 `@Size`
2023-04-04 11:32:39 +02:00
}
func(s Sized) String() string {
return fmt.Sprintf("Sized: %v %v", s.Sym, s.Size)
}
type Arg struct {
ArgDisplay *Display `@@?`
ArgSized *Sized `@@?`
2023-04-05 12:22:13 +02:00
ArgFlag *uint8 `@Size?`
2023-04-04 11:32:39 +02:00
ArgDouble *Double `@@?`
2023-04-05 09:12:13 +02:00
ArgSingle *Single `@@?`
2023-04-05 12:22:13 +02:00
ArgNone string `Whitespace? EOL`
2023-04-04 11:32:39 +02:00
}
func (a Arg) String() string {
if a.ArgDisplay != nil {
return fmt.Sprintf("%s", a.ArgDisplay)
}
2023-04-05 12:22:13 +02:00
if a.ArgFlag != nil {
return fmt.Sprintf("Flag: %v", *a.ArgFlag)
2023-04-05 09:59:24 +02:00
}
2023-04-04 11:32:39 +02:00
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]+`},
2023-04-05 09:59:24 +02:00
{"SizeSig", `[0-9]+\s+{?:[0-9]}`},
2023-04-04 11:32:39 +02:00
{"Size", `[0-9]+`},
2023-04-05 09:59:24 +02:00
{"Sym", `[a-zA-Z_][a-zA-Z0-9_]+`},
{"Whitespace", `[ \t]+`},
{"Discard", `^\s+[\n\r]+$`},
{"EOL", `[\n\r]+`},
2023-04-04 11:32:39 +02:00
{"Quote", `["']`},
})
asmParser = participle.MustBuild[Asm](
participle.Lexer(asmLexer),
participle.Elide("Comment", "Whitespace"),
)
)
2023-04-04 21:32:40 +02:00
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)
}
2023-04-04 22:02:17 +02:00
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)
}
2023-04-04 21:32:40 +02:00
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
2023-04-05 09:59:24 +02:00
return w.Write(bn[c:])
2023-04-04 21:32:40 +02:00
}
2023-04-05 09:14:08 +02:00
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
}
2023-04-05 09:14:30 +02:00
n, err = writeSym(v.One, b)
2023-04-05 09:14:08 +02:00
rn += n
if err != nil {
return rn, err
}
if w != nil {
rn, err = w.Write(b.Bytes())
} else {
rn = 0
}
return rn, err
}
2023-04-04 21:32:40 +02:00
2023-04-04 22:02:17 +02:00
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
}
2023-04-05 09:12:13 +02:00
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
}
2023-04-04 21:32:40 +02:00
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
}
2023-04-05 12:22:13 +02:00
func parseNoarg(op vm.Opcode, arg Arg, w io.Writer) (int, error) {
2023-04-05 09:59:24 +02:00
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
}
2023-04-05 12:22:13 +02:00
func parseFlag(op vm.Opcode, arg Arg, w io.Writer) (int, error) {
2023-04-05 09:21:46 +02:00
var rn int
2023-04-05 12:22:13 +02:00
var err error
2023-04-05 09:21:46 +02:00
2023-04-05 12:22:13 +02:00
v := arg.ArgFlag
if v == nil {
return 0, nil
2023-04-05 09:21:46 +02:00
}
if w != nil {
2023-04-05 12:22:13 +02:00
rn, err = w.Write([]byte{*v})
2023-04-05 09:21:46 +02:00
} else {
rn = 0
}
return rn, err
2023-04-05 12:22:13 +02:00
2023-04-05 09:21:46 +02:00
}
2023-04-04 11:32:39 +02:00
func Parse(s string, w io.Writer) (int, error) {
rd := strings.NewReader(s)
ast, err := asmParser.Parse("file", rd)
2023-04-05 09:38:07 +02:00
if err != nil {
return 0, err
}
2023-04-04 21:32:40 +02:00
2023-04-05 09:38:07 +02:00
var rn int
2023-04-04 21:32:40 +02:00
for _, v := range ast.Instructions {
2023-04-05 09:59:24 +02:00
log.Printf("parsing line %v: %v", v.OpCode, v.OpArg)
2023-04-04 11:38:01 +02:00
op := vm.OpcodeIndex[v.OpCode]
2023-04-04 21:32:40 +02:00
n, err := parseSized(op, v.OpArg, w)
if err != nil {
return n, err
}
if n > 0 {
rn += n
2023-04-05 12:22:13 +02:00
n, err = parseFlag(op, v.OpArg, w)
if err != nil {
return n, err
}
2023-04-04 22:02:17 +02:00
rn += n
continue
}
2023-04-05 12:22:13 +02:00
n, err = parseDisplay(op, v.OpArg, w)
2023-04-05 09:59:24 +02:00
if err != nil {
return n, err
}
if n > 0 {
rn += n
continue
}
2023-04-05 09:12:13 +02:00
n, err = parseDouble(op, v.OpArg, w)
if err != nil {
return n, err
}
if n > 0 {
rn += n
continue
}
2023-04-05 09:14:08 +02:00
n, err = parseSingle(op, v.OpArg, w)
if err != nil {
return n, err
}
if n > 0 {
rn += n
continue
}
2023-04-05 09:21:46 +02:00
n, err = parseNoarg(op, v.OpArg, w)
if err != nil {
return n, err
}
if n > 0 {
rn += n
continue
}
2023-04-04 11:32:39 +02:00
}
2023-04-04 21:32:40 +02:00
return rn, err
2023-04-04 11:32:39 +02:00
}