mirror of
https://github.com/zellyn/go6502.git
synced 2024-06-24 15:29:40 +00:00
Separated context out of Flavor
This commit is contained in:
parent
457446772a
commit
c7308bf05c
30
asm/asm.go
30
asm/asm.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/zellyn/go6502/asm/context"
|
||||||
"github.com/zellyn/go6502/asm/flavors"
|
"github.com/zellyn/go6502/asm/flavors"
|
||||||
"github.com/zellyn/go6502/asm/inst"
|
"github.com/zellyn/go6502/asm/inst"
|
||||||
"github.com/zellyn/go6502/asm/lines"
|
"github.com/zellyn/go6502/asm/lines"
|
||||||
|
@ -17,13 +18,17 @@ type Assembler struct {
|
||||||
Opener lines.Opener
|
Opener lines.Opener
|
||||||
Insts []*inst.I
|
Insts []*inst.I
|
||||||
Macros map[string]macros.M
|
Macros map[string]macros.M
|
||||||
|
Ctx *context.SimpleContext
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAssembler(flavor flavors.F, opener lines.Opener) *Assembler {
|
func NewAssembler(flavor flavors.F, opener lines.Opener) *Assembler {
|
||||||
|
ctx := &context.SimpleContext{}
|
||||||
|
flavor.InitContext(ctx)
|
||||||
return &Assembler{
|
return &Assembler{
|
||||||
Flavor: flavor,
|
Flavor: flavor,
|
||||||
Opener: opener,
|
Opener: opener,
|
||||||
Macros: make(map[string]macros.M),
|
Macros: make(map[string]macros.M),
|
||||||
|
Ctx: ctx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +52,7 @@ func (a *Assembler) Load(filename string, prefix int) error {
|
||||||
lineSources = lineSources[1:]
|
lineSources = lineSources[1:]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
in, parseErr := a.Flavor.ParseInstr(line, false)
|
in, parseErr := a.Flavor.ParseInstr(a.Ctx, line, false)
|
||||||
if len(ifdefs) > 0 && !ifdefs[0] && in.Type != inst.TypeIfdefElse && in.Type != inst.TypeIfdefEnd {
|
if len(ifdefs) > 0 && !ifdefs[0] && in.Type != inst.TypeIfdefElse && in.Type != inst.TypeIfdefEnd {
|
||||||
// we're in an inactive ifdef branch
|
// we're in an inactive ifdef branch
|
||||||
continue
|
continue
|
||||||
|
@ -83,17 +88,17 @@ func (a *Assembler) Load(filename string, prefix int) error {
|
||||||
return in.Errorf(`error calling macro "%s": %v`, m.Name, err)
|
return in.Errorf(`error calling macro "%s": %v`, m.Name, err)
|
||||||
}
|
}
|
||||||
lineSources = append([]lines.LineSource{subLs}, lineSources...)
|
lineSources = append([]lines.LineSource{subLs}, lineSources...)
|
||||||
a.Flavor.PushMacroCall(m.Name, macroCall, m.Locals)
|
a.Ctx.PushMacroCall(m.Name, macroCall, m.Locals)
|
||||||
case inst.TypeMacroEnd:
|
case inst.TypeMacroEnd:
|
||||||
// If we reached here, it's in a macro call, not a definition.
|
// If we reached here, it's in a macro call, not a definition.
|
||||||
if !a.Flavor.PopMacroCall() {
|
if !a.Ctx.PopMacroCall() {
|
||||||
return in.Errorf("unexpected end of macro")
|
return in.Errorf("unexpected end of macro")
|
||||||
}
|
}
|
||||||
case inst.TypeIfdef:
|
case inst.TypeIfdef:
|
||||||
if len(in.Exprs) == 0 {
|
if len(in.Exprs) == 0 {
|
||||||
panic(fmt.Sprintf("Ifdef got parsed with no expression: %s", line))
|
panic(fmt.Sprintf("Ifdef got parsed with no expression: %s", line))
|
||||||
}
|
}
|
||||||
val, err := in.Exprs[0].Eval(a.Flavor, in.Line)
|
val, err := in.Exprs[0].Eval(a.Ctx, in.Line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return in.Errorf("cannot eval ifdef condition: %v", err)
|
return in.Errorf("cannot eval ifdef condition: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -163,14 +168,14 @@ func (a *Assembler) readMacro(in inst.I, ls lines.LineSource) error {
|
||||||
if done {
|
if done {
|
||||||
return in.Errorf("end of file while reading macro %s", m.Name)
|
return in.Errorf("end of file while reading macro %s", m.Name)
|
||||||
}
|
}
|
||||||
in2, err := a.Flavor.ParseInstr(line, true)
|
in2, err := a.Flavor.ParseInstr(a.Ctx, line, true)
|
||||||
if a.Flavor.LocalMacroLabels() && in2.Label != "" {
|
if a.Flavor.LocalMacroLabels() && in2.Label != "" {
|
||||||
m.Locals[in2.Label] = true
|
m.Locals[in2.Label] = true
|
||||||
}
|
}
|
||||||
m.Lines = append(m.Lines, line.Parse.Text())
|
m.Lines = append(m.Lines, line.Parse.Text())
|
||||||
if err == nil && in2.Type == inst.TypeMacroEnd {
|
if err == nil && in2.Type == inst.TypeMacroEnd {
|
||||||
a.Macros[m.Name] = m
|
a.Macros[m.Name] = m
|
||||||
a.Flavor.AddMacroName(m.Name)
|
a.Ctx.AddMacroName(m.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,9 +183,9 @@ func (a *Assembler) readMacro(in inst.I, ls lines.LineSource) error {
|
||||||
|
|
||||||
// Clear out stuff that may be hanging around from the previous pass, set origin to default, etc.
|
// Clear out stuff that may be hanging around from the previous pass, set origin to default, etc.
|
||||||
func (a *Assembler) initPass() {
|
func (a *Assembler) initPass() {
|
||||||
a.Flavor.SetLastLabel("") // No last label (yet)
|
a.Ctx.SetLastLabel("") // No last label (yet)
|
||||||
a.Flavor.RemoveChanged() // Remove any variables whose value ever changed.
|
a.Ctx.RemoveChanged() // Remove any variables whose value ever changed.
|
||||||
a.Flavor.SetAddr(a.Flavor.DefaultOrigin())
|
a.Ctx.SetAddr(a.Flavor.DefaultOrigin())
|
||||||
}
|
}
|
||||||
|
|
||||||
// passInst performs a pass on a single instruction. It forces the
|
// passInst performs a pass on a single instruction. It forces the
|
||||||
|
@ -188,16 +193,15 @@ func (a *Assembler) initPass() {
|
||||||
// arguments. If final is true, and the instruction cannot be
|
// arguments. If final is true, and the instruction cannot be
|
||||||
// finalized, it returns an error.
|
// finalized, it returns an error.
|
||||||
func (a *Assembler) passInst(in *inst.I, final bool) (isFinal bool, err error) {
|
func (a *Assembler) passInst(in *inst.I, final bool) (isFinal bool, err error) {
|
||||||
// fmt.Printf("PLUGH: in.Compute(a.Flavor, true, true) on %s\n", in)
|
isFinal, err = in.Compute(a.Ctx, final)
|
||||||
isFinal, err = in.Compute(a.Flavor, final)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update address
|
// Update address
|
||||||
addr, _ := a.Flavor.GetAddr()
|
addr, _ := a.Ctx.GetAddr()
|
||||||
in.Addr = addr
|
in.Addr = addr
|
||||||
a.Flavor.SetAddr(addr + in.Width)
|
a.Ctx.SetAddr(addr + in.Width)
|
||||||
|
|
||||||
return isFinal, nil
|
return isFinal, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ type Context interface {
|
||||||
Get(name string) (uint16, bool)
|
Get(name string) (uint16, bool)
|
||||||
SetAddr(uint16)
|
SetAddr(uint16)
|
||||||
GetAddr() (uint16, bool)
|
GetAddr() (uint16, bool)
|
||||||
DivZero() (uint16, error)
|
DivZero() *uint16
|
||||||
|
SetDivZero(uint16)
|
||||||
RemoveChanged()
|
RemoveChanged()
|
||||||
Clear()
|
Clear()
|
||||||
SettingOn(name string) error
|
SettingOn(name string) error
|
||||||
|
@ -19,7 +20,7 @@ type Context interface {
|
||||||
PushMacroCall(name string, number int, locals map[string]bool)
|
PushMacroCall(name string, number int, locals map[string]bool)
|
||||||
PopMacroCall() bool
|
PopMacroCall() bool
|
||||||
GetMacroCall() (string, int, map[string]bool)
|
GetMacroCall() (string, int, map[string]bool)
|
||||||
|
SetOnOffDefaults(map[string]bool)
|
||||||
LastLabel() string
|
LastLabel() string
|
||||||
SetLastLabel(label string)
|
SetLastLabel(label string)
|
||||||
}
|
}
|
||||||
|
@ -39,6 +40,7 @@ type SimpleContext struct {
|
||||||
onOffDefaults map[string]bool
|
onOffDefaults map[string]bool
|
||||||
macroNames map[string]bool
|
macroNames map[string]bool
|
||||||
macroCalls []macroCall
|
macroCalls []macroCall
|
||||||
|
divZeroVal *uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
type symbolValue struct {
|
type symbolValue struct {
|
||||||
|
@ -52,10 +54,6 @@ func (sc *SimpleContext) fix() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *SimpleContext) DivZero() (uint16, error) {
|
|
||||||
return 0, fmt.Errorf("Not implemented: context.SimpleContext.DivZero()")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *SimpleContext) Get(name string) (uint16, bool) {
|
func (sc *SimpleContext) Get(name string) (uint16, bool) {
|
||||||
if name == "*" {
|
if name == "*" {
|
||||||
return sc.GetAddr()
|
return sc.GetAddr()
|
||||||
|
@ -185,3 +183,11 @@ func (sc *SimpleContext) LastLabel() string {
|
||||||
func (sc *SimpleContext) SetLastLabel(l string) {
|
func (sc *SimpleContext) SetLastLabel(l string) {
|
||||||
sc.lastLabel = l
|
sc.lastLabel = l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *SimpleContext) DivZero() *uint16 {
|
||||||
|
return sc.divZeroVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SimpleContext) SetDivZero(val uint16) {
|
||||||
|
sc.divZeroVal = &val
|
||||||
|
}
|
||||||
|
|
|
@ -174,7 +174,11 @@ func (e *E) Eval(ctx context.Context, ln *lines.Line) (uint16, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
case OpDiv:
|
case OpDiv:
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
return ctx.DivZero()
|
z := ctx.DivZero()
|
||||||
|
if z == nil {
|
||||||
|
return 0, ln.Errorf("divizion by zero")
|
||||||
|
}
|
||||||
|
return *z, nil
|
||||||
}
|
}
|
||||||
return l / r, nil
|
return l / r, nil
|
||||||
case OpAnd:
|
case OpAnd:
|
||||||
|
|
|
@ -11,16 +11,14 @@ import (
|
||||||
// AS65 implements the AS65-compatible assembler flavor.
|
// AS65 implements the AS65-compatible assembler flavor.
|
||||||
// See http://www.kingswood-consulting.co.uk/assemblers/
|
// See http://www.kingswood-consulting.co.uk/assemblers/
|
||||||
|
|
||||||
type AS65 struct {
|
type AS65 struct{}
|
||||||
context.SimpleContext
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() *AS65 {
|
func New() *AS65 {
|
||||||
return &AS65{}
|
return &AS65{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse an entire instruction, or return an appropriate error.
|
// Parse an entire instruction, or return an appropriate error.
|
||||||
func (a *AS65) ParseInstr(line lines.Line, quick bool) (inst.I, error) {
|
func (a *AS65) ParseInstr(ctx context.Context, line lines.Line, quick bool) (inst.I, error) {
|
||||||
return inst.I{}, nil
|
return inst.I{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,3 +45,6 @@ func (a *AS65) LocalMacroLabels() bool {
|
||||||
func (a *AS65) String() string {
|
func (a *AS65) String() string {
|
||||||
return "as65"
|
return "as65"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AS65) InitContext(ctx context.Context) {
|
||||||
|
}
|
||||||
|
|
|
@ -7,10 +7,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type F interface {
|
type F interface {
|
||||||
ParseInstr(Line lines.Line, quick bool) (inst.I, error)
|
ParseInstr(ctx context.Context, Line lines.Line, quick bool) (inst.I, error)
|
||||||
DefaultOrigin() uint16
|
DefaultOrigin() uint16
|
||||||
ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error)
|
ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error)
|
||||||
LocalMacroLabels() bool
|
LocalMacroLabels() bool
|
||||||
String() string
|
String() string
|
||||||
context.Context
|
InitContext(context.Context)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package merlin
|
package merlin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zellyn/go6502/asm/context"
|
||||||
"github.com/zellyn/go6502/asm/expr"
|
"github.com/zellyn/go6502/asm/expr"
|
||||||
"github.com/zellyn/go6502/asm/flavors/oldschool"
|
"github.com/zellyn/go6502/asm/flavors/oldschool"
|
||||||
"github.com/zellyn/go6502/asm/inst"
|
"github.com/zellyn/go6502/asm/inst"
|
||||||
|
@ -38,6 +38,8 @@ func New() *Merlin {
|
||||||
m.InvCharChars = `"`
|
m.InvCharChars = `"`
|
||||||
m.MacroArgSep = ";"
|
m.MacroArgSep = ";"
|
||||||
m.SuffixForWide = true
|
m.SuffixForWide = true
|
||||||
|
m.LocalMacroLabelsVal = true
|
||||||
|
m.DefaultOriginVal = 0x8000
|
||||||
|
|
||||||
m.Directives = map[string]oldschool.DirectiveInfo{
|
m.Directives = map[string]oldschool.DirectiveInfo{
|
||||||
"ORG": {inst.TypeOrg, m.ParseAddress, 0},
|
"ORG": {inst.TypeOrg, m.ParseAddress, 0},
|
||||||
|
@ -77,7 +79,8 @@ func New() *Merlin {
|
||||||
"!": expr.OpXor,
|
"!": expr.OpXor,
|
||||||
}
|
}
|
||||||
|
|
||||||
m.SetOnOffDefaults(map[string]bool{
|
m.InitContextFunc = func(ctx context.Context) {
|
||||||
|
ctx.SetOnOffDefaults(map[string]bool{
|
||||||
"LST": true, // Display listing: not used
|
"LST": true, // Display listing: not used
|
||||||
"XC": false, // Extended commands: not implemented yet
|
"XC": false, // Extended commands: not implemented yet
|
||||||
"EXP": false, // How to print macro calls
|
"EXP": false, // How to print macro calls
|
||||||
|
@ -85,8 +88,9 @@ func New() *Merlin {
|
||||||
"TR": false, // truncate listing to 3 bytes?
|
"TR": false, // truncate listing to 3 bytes?
|
||||||
"CYC": false, // print cycle times?
|
"CYC": false, // print cycle times?
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
m.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) {
|
m.SetAsciiVariation = func(ctx context.Context, in *inst.I, lp *lines.Parse) {
|
||||||
if in.Command != "ASC" && in.Command != "DCI" {
|
if in.Command != "ASC" && in.Command != "DCI" {
|
||||||
panic(fmt.Sprintf("Unimplemented/unknown ascii directive: '%s'", in.Command))
|
panic(fmt.Sprintf("Unimplemented/unknown ascii directive: '%s'", in.Command))
|
||||||
}
|
}
|
||||||
|
@ -108,11 +112,11 @@ func New() *Merlin {
|
||||||
// the "command column" value, which caused isMacroCall to return
|
// the "command column" value, which caused isMacroCall to return
|
||||||
// true, and the lp to be pointing to the following character
|
// true, and the lp to be pointing to the following character
|
||||||
// (probably whitespace).
|
// (probably whitespace).
|
||||||
m.ParseMacroCall = func(in inst.I, lp *lines.Parse) (inst.I, bool, error) {
|
m.ParseMacroCall = func(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, bool, error) {
|
||||||
if in.Command == "" {
|
if in.Command == "" {
|
||||||
return in, false, nil
|
return in, false, nil
|
||||||
}
|
}
|
||||||
byName := m.HasMacroName(in.Command)
|
byName := ctx.HasMacroName(in.Command)
|
||||||
|
|
||||||
// It's not a macro call.
|
// It's not a macro call.
|
||||||
if in.Command != ">>>" && in.Command != "PMC" && !byName {
|
if in.Command != ">>>" && in.Command != "PMC" && !byName {
|
||||||
|
@ -127,7 +131,7 @@ func New() *Merlin {
|
||||||
return in, true, in.Errorf("Expected macro name, got char '%c'", c)
|
return in, true, in.Errorf("Expected macro name, got char '%c'", c)
|
||||||
}
|
}
|
||||||
in.Command = lp.Emit()
|
in.Command = lp.Emit()
|
||||||
if !m.HasMacroName(in.Command) {
|
if !ctx.HasMacroName(in.Command) {
|
||||||
return in, true, in.Errorf("Unknown macro: '%s'", in.Command)
|
return in, true, in.Errorf("Unknown macro: '%s'", in.Command)
|
||||||
}
|
}
|
||||||
if !lp.Consume(" ./,-(") {
|
if !lp.Consume(" ./,-(") {
|
||||||
|
@ -153,13 +157,13 @@ func New() *Merlin {
|
||||||
return in, true, nil
|
return in, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m.FixLabel = func(label string) (string, error) {
|
m.FixLabel = func(ctx context.Context, label string) (string, error) {
|
||||||
_, macroCount, locals := m.GetMacroCall()
|
_, macroCount, locals := ctx.GetMacroCall()
|
||||||
switch {
|
switch {
|
||||||
case label == "":
|
case label == "":
|
||||||
return label, nil
|
return label, nil
|
||||||
case label[0] == ':':
|
case label[0] == ':':
|
||||||
if last := m.LastLabel(); last == "" {
|
if last := ctx.LastLabel(); last == "" {
|
||||||
return "", fmt.Errorf("sublabel '%s' without previous label", label)
|
return "", fmt.Errorf("sublabel '%s' without previous label", label)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%s/%s", last, label), nil
|
return fmt.Sprintf("%s/%s", last, label), nil
|
||||||
|
@ -178,15 +182,7 @@ func New() *Merlin {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Merlin) Zero() (uint16, error) {
|
func (m *Merlin) ParseInclude(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return 0, errors.New("Division by zero.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Merlin) DefaultOrigin() uint16 {
|
|
||||||
return 0x8000
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Merlin) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
|
|
||||||
lp.IgnoreRun(whitespace)
|
lp.IgnoreRun(whitespace)
|
||||||
lp.AcceptUntil(";")
|
lp.AcceptUntil(";")
|
||||||
filename := strings.TrimSpace(lp.Emit())
|
filename := strings.TrimSpace(lp.Emit())
|
||||||
|
@ -204,7 +200,3 @@ func (m *Merlin) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
in.Final = true
|
in.Final = true
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Merlin) LocalMacroLabels() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package oldschool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -25,7 +26,7 @@ const fileChars = Letters + Digits + "."
|
||||||
|
|
||||||
type DirectiveInfo struct {
|
type DirectiveInfo struct {
|
||||||
Type inst.Type
|
Type inst.Type
|
||||||
Func func(inst.I, *lines.Parse) (inst.I, error)
|
Func func(context.Context, inst.I, *lines.Parse) (inst.I, error)
|
||||||
Var inst.Variant
|
Var inst.Variant
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +44,6 @@ type Base struct {
|
||||||
Name string
|
Name string
|
||||||
Directives map[string]DirectiveInfo
|
Directives map[string]DirectiveInfo
|
||||||
Operators map[string]expr.Operator
|
Operators map[string]expr.Operator
|
||||||
context.SimpleContext
|
|
||||||
LabelChars string
|
LabelChars string
|
||||||
LabelColons Requiredness
|
LabelColons Requiredness
|
||||||
ExplicitARegister Requiredness
|
ExplicitARegister Requiredness
|
||||||
|
@ -60,12 +60,15 @@ type Base struct {
|
||||||
CharChars string
|
CharChars string
|
||||||
InvCharChars string
|
InvCharChars string
|
||||||
MacroArgSep string
|
MacroArgSep string
|
||||||
LocalMacroLabels bool
|
|
||||||
ExtraCommenty func(string) bool
|
ExtraCommenty func(string) bool
|
||||||
SetAsciiVariation func(*inst.I, *lines.Parse)
|
SetAsciiVariation func(context.Context, *inst.I, *lines.Parse)
|
||||||
ParseMacroCall func(inst.I, *lines.Parse) (inst.I, bool, error)
|
ParseMacroCall func(context.Context, inst.I, *lines.Parse) (inst.I, bool, error)
|
||||||
FixLabel func(label string) (string, error)
|
|
||||||
IsNewParentLabel func(label string) bool
|
IsNewParentLabel func(label string) bool
|
||||||
|
InitContextFunc func(context.Context)
|
||||||
|
FixLabel func(context.Context, string) (string, error)
|
||||||
|
LocalMacroLabelsVal bool
|
||||||
|
DivZeroVal *uint16
|
||||||
|
DefaultOriginVal uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) String() string {
|
func (a *Base) String() string {
|
||||||
|
@ -73,7 +76,7 @@ func (a *Base) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse an entire instruction, or return an appropriate error.
|
// Parse an entire instruction, or return an appropriate error.
|
||||||
func (a *Base) ParseInstr(line lines.Line, quick bool) (inst.I, error) {
|
func (a *Base) ParseInstr(ctx context.Context, line lines.Line, quick bool) (inst.I, error) {
|
||||||
lp := line.Parse
|
lp := line.Parse
|
||||||
in := inst.I{Line: &line}
|
in := inst.I{Line: &line}
|
||||||
|
|
||||||
|
@ -125,13 +128,13 @@ func (a *Base) ParseInstr(line lines.Line, quick bool) (inst.I, error) {
|
||||||
// If appropriate, set the last parent label.
|
// If appropriate, set the last parent label.
|
||||||
if !quick {
|
if !quick {
|
||||||
parent := a.IsNewParentLabel(in.Label)
|
parent := a.IsNewParentLabel(in.Label)
|
||||||
newL, err := a.FixLabel(in.Label)
|
newL, err := a.FixLabel(ctx, in.Label)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return in, in.Errorf("%v", err)
|
return in, in.Errorf("%v", err)
|
||||||
}
|
}
|
||||||
in.Label = newL
|
in.Label = newL
|
||||||
if parent {
|
if parent {
|
||||||
a.SetLastLabel(in.Label)
|
ctx.SetLastLabel(in.Label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,16 +145,12 @@ func (a *Base) ParseInstr(line lines.Line, quick bool) (inst.I, error) {
|
||||||
in.Type = inst.TypeNone
|
in.Type = inst.TypeNone
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
return a.parseCmd(in, lp, quick)
|
return a.parseCmd(ctx, in, lp, quick)
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Base) DefaultOrigin() uint16 {
|
|
||||||
return 0x0800
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseCmd parses the "command" part of an instruction: we expect to be
|
// parseCmd parses the "command" part of an instruction: we expect to be
|
||||||
// looking at a non-whitespace character.
|
// looking at a non-whitespace character.
|
||||||
func (a *Base) parseCmd(in inst.I, lp *lines.Parse, quick bool) (inst.I, error) {
|
func (a *Base) parseCmd(ctx context.Context, in inst.I, lp *lines.Parse, quick bool) (inst.I, error) {
|
||||||
if !lp.AcceptRun(cmdChars) && !(a.Directives["="].Func != nil && lp.Accept("=")) {
|
if !lp.AcceptRun(cmdChars) && !(a.Directives["="].Func != nil && lp.Accept("=")) {
|
||||||
c := lp.Next()
|
c := lp.Next()
|
||||||
return in, in.Errorf("expecting instruction, found '%c' (%d)", c, c)
|
return in, in.Errorf("expecting instruction, found '%c' (%d)", c, c)
|
||||||
|
@ -170,7 +169,7 @@ func (a *Base) parseCmd(in inst.I, lp *lines.Parse, quick bool) (inst.I, error)
|
||||||
}
|
}
|
||||||
// Give ParseMacroCall a chance
|
// Give ParseMacroCall a chance
|
||||||
if a.ParseMacroCall != nil {
|
if a.ParseMacroCall != nil {
|
||||||
i, isMacro, err := a.ParseMacroCall(in, lp)
|
i, isMacro, err := a.ParseMacroCall(ctx, in, lp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return in, err
|
return in, err
|
||||||
}
|
}
|
||||||
|
@ -185,16 +184,16 @@ func (a *Base) parseCmd(in inst.I, lp *lines.Parse, quick bool) (inst.I, error)
|
||||||
if dir.Func == nil {
|
if dir.Func == nil {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
return dir.Func(in, lp)
|
return dir.Func(ctx, in, lp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.HasSetting(in.Command) {
|
if ctx.HasSetting(in.Command) {
|
||||||
return a.parseSetting(in, lp)
|
return a.parseSetting(ctx, in, lp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if summary, ok := opcodes.ByName[in.Command]; ok {
|
if summary, ok := opcodes.ByName[in.Command]; ok {
|
||||||
in.Type = inst.TypeOp
|
in.Type = inst.TypeOp
|
||||||
return a.parseOpArgs(in, lp, summary, false)
|
return a.parseOpArgs(ctx, in, lp, summary, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merlin lets you say "LDA:" or "LDA@" or "LDAZ" to force non-zero-page.
|
// Merlin lets you say "LDA:" or "LDA@" or "LDAZ" to force non-zero-page.
|
||||||
|
@ -203,14 +202,14 @@ func (a *Base) parseCmd(in inst.I, lp *lines.Parse, quick bool) (inst.I, error)
|
||||||
if summary, ok := opcodes.ByName[prefix]; ok {
|
if summary, ok := opcodes.ByName[prefix]; ok {
|
||||||
in.Command = prefix
|
in.Command = prefix
|
||||||
in.Type = inst.TypeOp
|
in.Type = inst.TypeOp
|
||||||
return a.parseOpArgs(in, lp, summary, true)
|
return a.parseOpArgs(ctx, in, lp, summary, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return in, in.Errorf(`unknown command/instruction: "%s"`, in.Command)
|
return in, in.Errorf(`unknown command/instruction: "%s"`, in.Command)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) parseSetting(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) parseSetting(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
in.Type = inst.TypeSetting
|
in.Type = inst.TypeSetting
|
||||||
lp.IgnoreRun(Whitespace)
|
lp.IgnoreRun(Whitespace)
|
||||||
if !lp.AcceptRun(Letters) {
|
if !lp.AcceptRun(Letters) {
|
||||||
|
@ -220,9 +219,9 @@ func (a *Base) parseSetting(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
in.TextArg = lp.Emit()
|
in.TextArg = lp.Emit()
|
||||||
switch in.TextArg {
|
switch in.TextArg {
|
||||||
case "ON":
|
case "ON":
|
||||||
a.SettingOn(in.Command)
|
ctx.SettingOn(in.Command)
|
||||||
case "OFF":
|
case "OFF":
|
||||||
a.SettingOff(in.Command)
|
ctx.SettingOff(in.Command)
|
||||||
default:
|
default:
|
||||||
return in, in.Errorf("expecting ON/OFF, found '%s'", in.TextArg)
|
return in, in.Errorf("expecting ON/OFF, found '%s'", in.TextArg)
|
||||||
}
|
}
|
||||||
|
@ -269,7 +268,7 @@ func (a *Base) parseQuoted(in inst.I, lp *lines.Parse) (string, error) {
|
||||||
|
|
||||||
// parseOpArgs parses the arguments to an assembly op. We expect to be
|
// parseOpArgs parses the arguments to an assembly op. We expect to be
|
||||||
// looking at the first non-op character (probably whitespace)
|
// looking at the first non-op character (probably whitespace)
|
||||||
func (a *Base) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary, forceWide bool) (inst.I, error) {
|
func (a *Base) parseOpArgs(ctx context.Context, in inst.I, lp *lines.Parse, summary opcodes.OpSummary, forceWide bool) (inst.I, error) {
|
||||||
// MODE_IMPLIED: we don't really care what comes next: it's a comment.
|
// MODE_IMPLIED: we don't really care what comes next: it's a comment.
|
||||||
if summary.Modes == opcodes.MODE_IMPLIED {
|
if summary.Modes == opcodes.MODE_IMPLIED {
|
||||||
op := summary.Ops[0]
|
op := summary.Ops[0]
|
||||||
|
@ -314,7 +313,7 @@ func (a *Base) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
|
||||||
return in, in.Errorf("%s doesn't support any indirect modes", in.Command)
|
return in, in.Errorf("%s doesn't support any indirect modes", in.Command)
|
||||||
}
|
}
|
||||||
xy := '-'
|
xy := '-'
|
||||||
expr, err := a.ParseExpression(in, lp)
|
expr, err := a.parseExpression(ctx, in, lp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return in, err
|
return in, err
|
||||||
}
|
}
|
||||||
|
@ -371,12 +370,12 @@ func (a *Base) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return common.DecodeOp(a, in, summary, indirect, xy, forceWide)
|
return common.DecodeOp(ctx, in, summary, indirect, xy, forceWide)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseAddress(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseAddress(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
lp.IgnoreRun(Whitespace)
|
lp.IgnoreRun(Whitespace)
|
||||||
expr, err := a.ParseExpression(in, lp)
|
expr, err := a.parseExpression(ctx, in, lp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return in, err
|
return in, err
|
||||||
}
|
}
|
||||||
|
@ -387,9 +386,9 @@ func (a *Base) ParseAddress(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseAscii(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
lp.IgnoreRun(Whitespace)
|
lp.IgnoreRun(Whitespace)
|
||||||
a.SetAsciiVariation(&in, lp)
|
a.SetAsciiVariation(ctx, &in, lp)
|
||||||
var invert, invertLast byte
|
var invert, invertLast byte
|
||||||
switch in.Var {
|
switch in.Var {
|
||||||
case inst.VarAscii:
|
case inst.VarAscii:
|
||||||
|
@ -424,9 +423,9 @@ func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseBlockStorage(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseBlockStorage(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
lp.IgnoreRun(Whitespace)
|
lp.IgnoreRun(Whitespace)
|
||||||
ex, err := a.ParseExpression(in, lp)
|
ex, err := a.parseExpression(ctx, in, lp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return in, err
|
return in, err
|
||||||
}
|
}
|
||||||
|
@ -434,10 +433,10 @@ func (a *Base) ParseBlockStorage(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseData(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseData(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
lp.IgnoreRun(Whitespace)
|
lp.IgnoreRun(Whitespace)
|
||||||
for {
|
for {
|
||||||
ex, err := a.ParseExpression(in, lp)
|
ex, err := a.parseExpression(ctx, in, lp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return in, err
|
return in, err
|
||||||
}
|
}
|
||||||
|
@ -449,9 +448,9 @@ func (a *Base) ParseData(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseDo(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseDo(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
lp.IgnoreRun(Whitespace)
|
lp.IgnoreRun(Whitespace)
|
||||||
expr, err := a.ParseExpression(in, lp)
|
expr, err := a.parseExpression(ctx, in, lp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return in, err
|
return in, err
|
||||||
}
|
}
|
||||||
|
@ -462,12 +461,18 @@ func (a *Base) ParseDo(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseEquate(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseEquate(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
lp.IgnoreRun(Whitespace)
|
lp.IgnoreRun(Whitespace)
|
||||||
expr, err := a.ParseExpression(in, lp)
|
expr, err := a.parseExpression(ctx, in, lp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return in, err
|
return in, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xyzzy, err := expr.Eval(ctx, in.Line)
|
||||||
|
if err != nil {
|
||||||
|
return in, err
|
||||||
|
}
|
||||||
|
_ = xyzzy
|
||||||
in.Exprs = append(in.Exprs, expr)
|
in.Exprs = append(in.Exprs, expr)
|
||||||
in.WidthKnown = true
|
in.WidthKnown = true
|
||||||
in.Width = 0
|
in.Width = 0
|
||||||
|
@ -475,7 +480,7 @@ func (a *Base) ParseEquate(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseHexString(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseHexString(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
lp.AcceptRun(Whitespace)
|
lp.AcceptRun(Whitespace)
|
||||||
for {
|
for {
|
||||||
lp.Ignore()
|
lp.Ignore()
|
||||||
|
@ -500,7 +505,7 @@ func (a *Base) ParseHexString(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseInclude(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
lp.IgnoreRun(Whitespace)
|
lp.IgnoreRun(Whitespace)
|
||||||
if !lp.AcceptRun(fileChars) {
|
if !lp.AcceptRun(fileChars) {
|
||||||
return in, in.Errorf("Expecting filename, found '%c'", lp.Next())
|
return in, in.Errorf("Expecting filename, found '%c'", lp.Next())
|
||||||
|
@ -513,7 +518,7 @@ func (a *Base) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// For assemblers where the macro name follows the macro directive.
|
// For assemblers where the macro name follows the macro directive.
|
||||||
func (a *Base) ParseMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseMacroStart(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
lp.IgnoreRun(Whitespace)
|
lp.IgnoreRun(Whitespace)
|
||||||
if !lp.AcceptRun(cmdChars) {
|
if !lp.AcceptRun(cmdChars) {
|
||||||
return in, in.Errorf("Expecting valid macro name, found '%c'", lp.Next())
|
return in, in.Errorf("Expecting valid macro name, found '%c'", lp.Next())
|
||||||
|
@ -526,7 +531,7 @@ func (a *Base) ParseMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// For assemblers where the macro name is the label, followed by the directive.
|
// For assemblers where the macro name is the label, followed by the directive.
|
||||||
func (a *Base) MarkMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) MarkMacroStart(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
in.TextArg, in.Label = in.Label, ""
|
in.TextArg, in.Label = in.Label, ""
|
||||||
in.WidthKnown = true
|
in.WidthKnown = true
|
||||||
in.Width = 0
|
in.Width = 0
|
||||||
|
@ -534,18 +539,18 @@ func (a *Base) MarkMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseNoArgDir(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseNoArgDir(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
in.WidthKnown = true
|
in.WidthKnown = true
|
||||||
in.Width = 0
|
in.Width = 0
|
||||||
in.Final = true
|
in.Final = true
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseNotImplemented(in inst.I, lp *lines.Parse) (inst.I, error) {
|
func (a *Base) ParseNotImplemented(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||||
return in, in.Errorf("not implemented (yet?): %s", in.Command)
|
return in, in.Errorf("not implemented (yet?): %s", in.Command)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseExpression(in inst.I, lp *lines.Parse) (*expr.E, error) {
|
func (a *Base) parseExpression(ctx context.Context, in inst.I, lp *lines.Parse) (*expr.E, error) {
|
||||||
|
|
||||||
if a.operatorChars == "" {
|
if a.operatorChars == "" {
|
||||||
for k, _ := range a.Operators {
|
for k, _ := range a.Operators {
|
||||||
|
@ -581,14 +586,14 @@ func (a *Base) ParseExpression(in inst.I, lp *lines.Parse) (*expr.E, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tree, err := a.ParseTerm(in, lp)
|
tree, err := a.ParseTerm(ctx, in, lp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &expr.E{}, err
|
return &expr.E{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for lp.Accept(a.operatorChars) {
|
for lp.Accept(a.operatorChars) {
|
||||||
c := lp.Emit()
|
c := lp.Emit()
|
||||||
right, err := a.ParseTerm(in, lp)
|
right, err := a.ParseTerm(ctx, in, lp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &expr.E{}, err
|
return &expr.E{}, err
|
||||||
}
|
}
|
||||||
|
@ -602,7 +607,7 @@ func (a *Base) ParseExpression(in inst.I, lp *lines.Parse) (*expr.E, error) {
|
||||||
return tree, nil
|
return tree, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) ParseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) {
|
func (a *Base) ParseTerm(ctx context.Context, in inst.I, lp *lines.Parse) (*expr.E, error) {
|
||||||
ex := &expr.E{}
|
ex := &expr.E{}
|
||||||
top := ex
|
top := ex
|
||||||
|
|
||||||
|
@ -688,7 +693,7 @@ func (a *Base) ParseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) {
|
||||||
|
|
||||||
ex.Op = expr.OpLeaf
|
ex.Op = expr.OpLeaf
|
||||||
ex.Text = lp.Emit()
|
ex.Text = lp.Emit()
|
||||||
newL, err := a.FixLabel(ex.Text)
|
newL, err := a.FixLabel(ctx, ex.Text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &expr.E{}, in.Errorf("%v", err)
|
return &expr.E{}, in.Errorf("%v", err)
|
||||||
}
|
}
|
||||||
|
@ -715,18 +720,18 @@ func (a *Base) DefaultIsNewParentLabel(label string) bool {
|
||||||
return label != "" && label[0] != '.'
|
return label != "" && label[0] != '.'
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Base) DefaultFixLabel(label string) (string, error) {
|
func (a *Base) DefaultFixLabel(ctx context.Context, label string) (string, error) {
|
||||||
switch {
|
switch {
|
||||||
case label == "":
|
case label == "":
|
||||||
return label, nil
|
return label, nil
|
||||||
case label[0] == '.':
|
case label[0] == '.':
|
||||||
if last := a.LastLabel(); last == "" {
|
if last := ctx.LastLabel(); last == "" {
|
||||||
return "", fmt.Errorf("sublabel '%s' without previous label", label)
|
return "", fmt.Errorf("sublabel '%s' without previous label", label)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%s/%s", last, label), nil
|
return fmt.Sprintf("%s/%s", last, label), nil
|
||||||
}
|
}
|
||||||
case label[0] == ':':
|
case label[0] == ':':
|
||||||
_, macroCall, _ := a.GetMacroCall()
|
_, macroCall, _ := ctx.GetMacroCall()
|
||||||
if macroCall == 0 {
|
if macroCall == 0 {
|
||||||
return "", fmt.Errorf("macro-local label '%s' seen outside macro", label)
|
return "", fmt.Errorf("macro-local label '%s' seen outside macro", label)
|
||||||
} else {
|
} else {
|
||||||
|
@ -735,3 +740,24 @@ func (a *Base) DefaultFixLabel(label string) (string, error) {
|
||||||
}
|
}
|
||||||
return label, nil
|
return label, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Base) LocalMacroLabels() bool {
|
||||||
|
return a.LocalMacroLabelsVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Base) Zero() (uint16, error) {
|
||||||
|
if a.DivZeroVal == nil {
|
||||||
|
return 0, errors.New("Division by zero.")
|
||||||
|
}
|
||||||
|
return *a.DivZeroVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Base) DefaultOrigin() uint16 {
|
||||||
|
return a.DefaultOriginVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Base) InitContext(ctx context.Context) {
|
||||||
|
if a.InitContextFunc != nil {
|
||||||
|
a.InitContextFunc(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package redbook
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/zellyn/go6502/asm/context"
|
||||||
"github.com/zellyn/go6502/asm/expr"
|
"github.com/zellyn/go6502/asm/expr"
|
||||||
"github.com/zellyn/go6502/asm/flavors/oldschool"
|
"github.com/zellyn/go6502/asm/flavors/oldschool"
|
||||||
"github.com/zellyn/go6502/asm/inst"
|
"github.com/zellyn/go6502/asm/inst"
|
||||||
|
@ -37,6 +38,7 @@ func newRedbook(name string) *RedBook {
|
||||||
r.MsbChars = "/"
|
r.MsbChars = "/"
|
||||||
r.ImmediateChars = "#"
|
r.ImmediateChars = "#"
|
||||||
r.HexCommas = oldschool.ReqOptional
|
r.HexCommas = oldschool.ReqOptional
|
||||||
|
r.DefaultOriginVal = 0x0800
|
||||||
|
|
||||||
r.Directives = map[string]oldschool.DirectiveInfo{
|
r.Directives = map[string]oldschool.DirectiveInfo{
|
||||||
"ORG": {inst.TypeOrg, r.ParseAddress, 0},
|
"ORG": {inst.TypeOrg, r.ParseAddress, 0},
|
||||||
|
@ -67,14 +69,16 @@ func newRedbook(name string) *RedBook {
|
||||||
"=": expr.OpEq,
|
"=": expr.OpEq,
|
||||||
}
|
}
|
||||||
|
|
||||||
r.SetOnOffDefaults(map[string]bool{
|
r.InitContextFunc = func(ctx context.Context) {
|
||||||
|
ctx.SetOnOffDefaults(map[string]bool{
|
||||||
"MSB": true, // MSB defaults to true, as per manual
|
"MSB": true, // MSB defaults to true, as per manual
|
||||||
"LST": true, // Display listing: not used
|
"LST": true, // Display listing: not used
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
r.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) {
|
r.SetAsciiVariation = func(ctx context.Context, in *inst.I, lp *lines.Parse) {
|
||||||
if in.Command == "ASC" {
|
if in.Command == "ASC" {
|
||||||
if r.Setting("MSB") {
|
if ctx.Setting("MSB") {
|
||||||
in.Var = inst.VarAsciiHi
|
in.Var = inst.VarAsciiHi
|
||||||
} else {
|
} else {
|
||||||
in.Var = inst.VarAscii
|
in.Var = inst.VarAscii
|
||||||
|
@ -93,7 +97,3 @@ func newRedbook(name string) *RedBook {
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RedBook) LocalMacroLabels() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package scma
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zellyn/go6502/asm/context"
|
||||||
"github.com/zellyn/go6502/asm/expr"
|
"github.com/zellyn/go6502/asm/expr"
|
||||||
"github.com/zellyn/go6502/asm/flavors/oldschool"
|
"github.com/zellyn/go6502/asm/flavors/oldschool"
|
||||||
"github.com/zellyn/go6502/asm/inst"
|
"github.com/zellyn/go6502/asm/inst"
|
||||||
|
@ -29,6 +30,9 @@ func New() *SCMA {
|
||||||
a.ImmediateChars = "#"
|
a.ImmediateChars = "#"
|
||||||
a.CharChars = "'"
|
a.CharChars = "'"
|
||||||
a.MacroArgSep = ","
|
a.MacroArgSep = ","
|
||||||
|
a.DefaultOriginVal = 0x0800
|
||||||
|
divZeroVal := uint16(0xffff)
|
||||||
|
a.DivZeroVal = &divZeroVal
|
||||||
|
|
||||||
a.Directives = map[string]oldschool.DirectiveInfo{
|
a.Directives = map[string]oldschool.DirectiveInfo{
|
||||||
".IN": {inst.TypeInclude, a.ParseInclude, 0},
|
".IN": {inst.TypeInclude, a.ParseInclude, 0},
|
||||||
|
@ -67,7 +71,7 @@ func New() *SCMA {
|
||||||
return strings.HasPrefix(s, commentWhitespacePrefix)
|
return strings.HasPrefix(s, commentWhitespacePrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) {
|
a.SetAsciiVariation = func(ctx context.Context, in *inst.I, lp *lines.Parse) {
|
||||||
// For S-C Assembler, leading "-" flips high bit
|
// For S-C Assembler, leading "-" flips high bit
|
||||||
invert := lp.Consume("-")
|
invert := lp.Consume("-")
|
||||||
invertLast := in.Command == ".AT"
|
invertLast := in.Command == ".AT"
|
||||||
|
@ -87,7 +91,7 @@ func New() *SCMA {
|
||||||
// the "command column" value, which caused isMacroCall to return
|
// the "command column" value, which caused isMacroCall to return
|
||||||
// true, and the lp to be pointing to the following character
|
// true, and the lp to be pointing to the following character
|
||||||
// (probably whitespace).
|
// (probably whitespace).
|
||||||
a.ParseMacroCall = func(in inst.I, lp *lines.Parse) (inst.I, bool, error) {
|
a.ParseMacroCall = func(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, bool, error) {
|
||||||
if in.Command == "" || in.Command[0] != '>' {
|
if in.Command == "" || in.Command[0] != '>' {
|
||||||
// not a macro call
|
// not a macro call
|
||||||
return in, false, nil
|
return in, false, nil
|
||||||
|
@ -117,11 +121,3 @@ func New() *SCMA {
|
||||||
|
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *SCMA) Zero() (uint16, error) {
|
|
||||||
return uint16(0xffff), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *SCMA) LocalMacroLabels() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package flavors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -24,15 +23,23 @@ func h(s string) []byte {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type asmFactory func() *asm.Assembler
|
||||||
|
|
||||||
func TestMultiline(t *testing.T) {
|
func TestMultiline(t *testing.T) {
|
||||||
o := lines.NewTestOpener()
|
o := lines.NewTestOpener()
|
||||||
|
|
||||||
ss := asm.NewAssembler(scma.New(), o)
|
ss := asmFactory(func() *asm.Assembler {
|
||||||
ra := asm.NewAssembler(redbook.NewRedbookA(), o)
|
return asm.NewAssembler(scma.New(), o)
|
||||||
mm := asm.NewAssembler(merlin.New(), o)
|
})
|
||||||
|
ra := asmFactory(func() *asm.Assembler {
|
||||||
|
return asm.NewAssembler(redbook.NewRedbookA(), o)
|
||||||
|
})
|
||||||
|
mm := asmFactory(func() *asm.Assembler {
|
||||||
|
return asm.NewAssembler(merlin.New(), o)
|
||||||
|
})
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
a *asm.Assembler // assembler
|
af asmFactory // assembler factory
|
||||||
name string // name
|
name string // name
|
||||||
i []string // main file: lines
|
i []string // main file: lines
|
||||||
ii map[string][]string // other files: lines
|
ii map[string][]string // other files: lines
|
||||||
|
@ -293,42 +300,41 @@ func TestMultiline(t *testing.T) {
|
||||||
if !tt.active {
|
if !tt.active {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Println(tt.name)
|
a := tt.af()
|
||||||
if tt.b == "" && len(tt.ps) == 0 {
|
if tt.b == "" && len(tt.ps) == 0 {
|
||||||
t.Fatalf(`%d("%s" - %s): test case must specify bytes or pieces`, i, tt.name, tt.a.Flavor)
|
t.Fatalf(`%d("%s" - %s): test case must specify bytes or pieces`, i, tt.name, a.Flavor)
|
||||||
}
|
}
|
||||||
tt.a.Reset()
|
|
||||||
o.Clear()
|
o.Clear()
|
||||||
o["TESTFILE"] = strings.Join(tt.i, "\n")
|
o["TESTFILE"] = strings.Join(tt.i, "\n")
|
||||||
for k, v := range tt.ii {
|
for k, v := range tt.ii {
|
||||||
o[k] = strings.Join(v, "\n")
|
o[k] = strings.Join(v, "\n")
|
||||||
}
|
}
|
||||||
if err := tt.a.Load("TESTFILE", 0); err != nil {
|
if err := a.Load("TESTFILE", 0); err != nil {
|
||||||
t.Fatalf(`%d("%s" - %s): tt.a.Load("TESTFILE") failed: %s`, i, tt.name, tt.a.Flavor, err)
|
t.Fatalf(`%d("%s" - %s): a.Load("TESTFILE") failed: %s`, i, tt.name, a.Flavor, err)
|
||||||
}
|
}
|
||||||
err := tt.a.Pass2()
|
err := a.Pass2()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(`%d("%s" - %s): tt.a.Pass(true) failed: %s`, i, tt.name, tt.a.Flavor, err)
|
t.Fatalf(`%d("%s" - %s): a.Pass(true) failed: %s`, i, tt.name, a.Flavor, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt.b != "" {
|
if tt.b != "" {
|
||||||
bb, err := tt.a.RawBytes()
|
bb, err := a.RawBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(`%d("%s" - %s): tt.a.RawBytes() failed: %s`, i, tt.name, tt.a.Flavor, err)
|
t.Fatalf(`%d("%s" - %s): a.RawBytes() failed: %s`, i, tt.name, a.Flavor, err)
|
||||||
}
|
}
|
||||||
hx := hex.EncodeToString(bb)
|
hx := hex.EncodeToString(bb)
|
||||||
if hx != tt.b {
|
if hx != tt.b {
|
||||||
t.Fatalf(`%d("%s" - %s): tt.a.RawBytes()=[%s]; want [%s]`, i, tt.name, tt.a.Flavor, hx, tt.b)
|
t.Fatalf(`%d("%s" - %s): a.RawBytes()=[%s]; want [%s]`, i, tt.name, a.Flavor, hx, tt.b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(tt.ps) != 0 {
|
if len(tt.ps) != 0 {
|
||||||
m, err := tt.a.Membuf()
|
m, err := a.Membuf()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(`%d("%s" - %s): tt.a.Membuf() failed: %s`, i, tt.name, tt.a.Flavor, err)
|
t.Fatalf(`%d("%s" - %s): a.Membuf() failed: %s`, i, tt.name, a.Flavor, err)
|
||||||
}
|
}
|
||||||
ps := m.Pieces()
|
ps := m.Pieces()
|
||||||
if !reflect.DeepEqual(ps, tt.ps) {
|
if !reflect.DeepEqual(ps, tt.ps) {
|
||||||
t.Fatalf(`%d("%s" - %s): tt.Membuf().Pieces() = %v; want %v`, i, tt.name, tt.a.Flavor, ps, tt.ps)
|
t.Fatalf(`%d("%s" - %s): tt.Membuf().Pieces() = %v; want %v`, i, tt.name, a.Flavor, ps, tt.ps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/zellyn/go6502/asm/context"
|
||||||
"github.com/zellyn/go6502/asm/flavors"
|
"github.com/zellyn/go6502/asm/flavors"
|
||||||
"github.com/zellyn/go6502/asm/flavors/as65"
|
"github.com/zellyn/go6502/asm/flavors/as65"
|
||||||
"github.com/zellyn/go6502/asm/flavors/merlin"
|
"github.com/zellyn/go6502/asm/flavors/merlin"
|
||||||
|
@ -20,7 +21,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
|
||||||
mm := merlin.New()
|
mm := merlin.New()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
a flavors.F // assembler flavor
|
f flavors.F // assembler flavor
|
||||||
i string // input string
|
i string // input string
|
||||||
p string // printed instruction, expected
|
p string // printed instruction, expected
|
||||||
b string // bytes, expected
|
b string // bytes, expected
|
||||||
|
@ -209,6 +210,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
|
||||||
{ss, ` .AT -DABCD`, "{data/b}", "c1c243"},
|
{ss, ` .AT -DABCD`, "{data/b}", "c1c243"},
|
||||||
{ss, ` .AT /ABC/`, "{data/b}", "4142c3"},
|
{ss, ` .AT /ABC/`, "{data/b}", "4142c3"},
|
||||||
{ss, `>SAM AB,$12,"A B","A, B, "" C"`, `{call SAM {"AB", "$12", "A B", "A, B, \" C"}}`, ""},
|
{ss, `>SAM AB,$12,"A B","A, B, "" C"`, `{call SAM {"AB", "$12", "A B", "A, B, \" C"}}`, ""},
|
||||||
|
// {ss, " LDA #3/0", "{LDA/imm (lsb (/ $0003 $0000))}", "a9ff"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(zellyn): Add tests for finalization of four SCMA directives:
|
// TODO(zellyn): Add tests for finalization of four SCMA directives:
|
||||||
|
@ -220,30 +222,32 @@ func TestSimpleCommonFunctions(t *testing.T) {
|
||||||
// TODO(zellyn): Test AS65 and Merlin too.
|
// TODO(zellyn): Test AS65 and Merlin too.
|
||||||
|
|
||||||
// Initialize to a known state for testing.
|
// Initialize to a known state for testing.
|
||||||
tt.a.Clear()
|
ctx := &context.SimpleContext{}
|
||||||
tt.a.SetAddr(0x2345)
|
ctx.Clear()
|
||||||
tt.a.Set("A.B", 0x6789)
|
ctx.SetAddr(0x2345)
|
||||||
tt.a.Set("C.D", 0x789a)
|
ctx.Set("A.B", 0x6789)
|
||||||
tt.a.Set("L2", 0x6789)
|
ctx.Set("C.D", 0x789a)
|
||||||
tt.a.Set("L3", 0x789a)
|
ctx.Set("L2", 0x6789)
|
||||||
tt.a.AddMacroName("INCW")
|
ctx.Set("L3", 0x789a)
|
||||||
tt.a.AddMacroName("M1")
|
ctx.AddMacroName("INCW")
|
||||||
|
ctx.AddMacroName("M1")
|
||||||
|
tt.f.InitContext(ctx)
|
||||||
|
|
||||||
inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i), false)
|
inst, err := tt.f.ParseInstr(ctx, lines.NewSimple(tt.i), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf(`%d. %s.ParseInstr("%s") => error: %s`, i, tt.a, tt.i, err)
|
t.Errorf(`%d. %s.ParseInstr("%s") => error: %s`, i, tt.f, tt.i, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if inst.Line.Parse == nil {
|
if inst.Line.Parse == nil {
|
||||||
t.Errorf("Got empty inst.Line.Parse on input '%s'", tt.i)
|
t.Errorf("Got empty inst.Line.Parse on input '%s'", tt.i)
|
||||||
}
|
}
|
||||||
_, err = inst.Compute(tt.a, true)
|
_, err = inst.Compute(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf(`%d. %s.ParseInstr("%s"): %s.Compute(tt.a, true) => error: %s`, i, tt.a, tt.i, inst, err)
|
t.Errorf(`%d. %s.ParseInstr("%s"): %s.Compute(tt.f, true) => error: %s`, i, tt.f, tt.i, inst, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if inst.String() != tt.p {
|
if inst.String() != tt.p {
|
||||||
t.Errorf(`%d. %s.ParseInstr("%s") = %s; want %s`, i, tt.a, tt.i, inst.String(), tt.p)
|
t.Errorf(`%d. %s.ParseInstr("%s") = %s; want %s`, i, tt.f, tt.i, inst.String(), tt.p)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +255,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
|
||||||
hx := hex.EncodeToString(inst.Data)
|
hx := hex.EncodeToString(inst.Data)
|
||||||
// xxxxxx sets the width, but doesn't expect actual data
|
// xxxxxx sets the width, but doesn't expect actual data
|
||||||
if hx != tt.b && (len(tt.b) == 0 || tt.b[0] != 'x') {
|
if hx != tt.b && (len(tt.b) == 0 || tt.b[0] != 'x') {
|
||||||
t.Errorf(`%d. %s.ParseInstr("%s").Data = [%s]; want [%s]`, i, tt.a, tt.i, hx, tt.b)
|
t.Errorf(`%d. %s.ParseInstr("%s").Data = [%s]; want [%s]`, i, tt.f, tt.i, hx, tt.b)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +279,7 @@ func TestSimpleErrors(t *testing.T) {
|
||||||
mm := merlin.New()
|
mm := merlin.New()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
a flavors.F // assembler flavor
|
f flavors.F // assembler flavor
|
||||||
i string // input string
|
i string // input string
|
||||||
}{
|
}{
|
||||||
|
|
||||||
|
@ -289,13 +293,13 @@ func TestSimpleErrors(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
// TODO(zellyn): Test AS65 and Merlin too.
|
if tt.f != ss {
|
||||||
if tt.a != ss {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i), false)
|
ctx := &context.SimpleContext{}
|
||||||
|
inst, err := tt.f.ParseInstr(ctx, lines.NewSimple(tt.i), false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf(`%d. %s.ParseInstr("%s") want err; got %s`, i, tt.a, tt.i, inst)
|
t.Errorf(`%d. %s.ParseInstr("%s") want err; got %s`, i, tt.f, tt.i, inst)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user