mirror of
https://github.com/zellyn/go6502.git
synced 2024-12-27 19:30:01 +00:00
scma: assembles applesoft without erroring
This commit is contained in:
parent
42bde82f34
commit
a241c48657
41
asm/asm.go
41
asm/asm.go
@ -11,6 +11,7 @@ import (
|
||||
type Flavor interface {
|
||||
ParseInstr(Line lines.Line) (inst.I, error)
|
||||
DefaultOrigin() (uint16, error)
|
||||
SetWidthsOnFirstPass() bool
|
||||
context.Context
|
||||
}
|
||||
|
||||
@ -56,49 +57,49 @@ func (a *Assembler) Load(filename string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.passInst(&in, false, false); err != nil {
|
||||
if _, err := a.passInst(&in, a.Flavor.SetWidthsOnFirstPass(), false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch in.Type {
|
||||
case inst.TypeUnknown:
|
||||
return fmt.Errorf("Unknown instruction: %s", line)
|
||||
return in.Errorf("unknown instruction: %s", line)
|
||||
case inst.TypeMacroStart:
|
||||
return fmt.Errorf("Macro start not (yet) implemented: %s", line)
|
||||
return in.Errorf("macro start not (yet) implemented: %s", line)
|
||||
case inst.TypeMacroCall:
|
||||
return fmt.Errorf("Macro call not (yet) implemented: %s", line)
|
||||
return in.Errorf("macro call not (yet) implemented: %s", line)
|
||||
case inst.TypeIfdef:
|
||||
if len(in.Exprs) == 0 {
|
||||
panic(fmt.Sprintf("Ifdef got parsed with no expression: %s", line))
|
||||
}
|
||||
val, err := in.Exprs[0].Eval(a.Flavor)
|
||||
val, err := in.Exprs[0].Eval(a.Flavor, in.Line)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot eval ifdef condition: %v", err)
|
||||
return in.Errorf("cannot eval ifdef condition: %v", err)
|
||||
}
|
||||
ifdefs = append([]bool{val != 0}, ifdefs...)
|
||||
|
||||
case inst.TypeIfdefElse:
|
||||
if len(ifdefs) == 0 {
|
||||
return fmt.Errorf("Ifdef else branch encountered outside ifdef: %s", line)
|
||||
return in.Errorf("ifdef else branch encountered outside ifdef: %s", line)
|
||||
}
|
||||
ifdefs[0] = !ifdefs[0]
|
||||
case inst.TypeIfdefEnd:
|
||||
if len(ifdefs) == 0 {
|
||||
return fmt.Errorf("Ifdef end encountered outside ifdef: %s", line)
|
||||
return in.Errorf("ifdef end encountered outside ifdef: %s", line)
|
||||
}
|
||||
ifdefs = ifdefs[1:]
|
||||
case inst.TypeInclude:
|
||||
subContext := lines.Context{Filename: in.TextArg, Parent: in.Line}
|
||||
subLs, err := lines.NewFileLineSource(in.TextArg, subContext, a.Opener)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error including file: %v", err)
|
||||
return in.Errorf("error including file: %v", err)
|
||||
}
|
||||
lineSources = append([]lines.LineSource{subLs}, lineSources...)
|
||||
continue // no need to append
|
||||
case inst.TypeTarget:
|
||||
return fmt.Errorf("Target not (yet) implemented: %s", line)
|
||||
return in.Errorf("target not (yet) implemented: %s", line)
|
||||
case inst.TypeSegment:
|
||||
return fmt.Errorf("Segment not (yet) implemented: %s", line)
|
||||
return in.Errorf("segment not (yet) implemented: %s", line)
|
||||
case inst.TypeEnd:
|
||||
return nil
|
||||
default:
|
||||
@ -115,7 +116,7 @@ func (a *Assembler) initPass() {
|
||||
if org, err := a.Flavor.DefaultOrigin(); err == nil {
|
||||
a.Flavor.SetAddr(org)
|
||||
} else {
|
||||
a.Flavor.ClearAddr()
|
||||
a.Flavor.ClearAddr("beginning of assembly")
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,9 +126,9 @@ func (a *Assembler) initPass() {
|
||||
// instruction to decide its final width. If final is true, and the
|
||||
// instruction cannot be finalized, it returns an error.
|
||||
func (a *Assembler) passInst(in *inst.I, setWidth, final bool) (isFinal bool, err error) {
|
||||
fmt.Printf("PLUGH: in.Compute(a.Flavor, true, true) on %s\n", in)
|
||||
// fmt.Printf("PLUGH: in.Compute(a.Flavor, true, true) on %s\n", in)
|
||||
isFinal, err = in.Compute(a.Flavor, setWidth, final)
|
||||
fmt.Printf("PLUGH: isFinal=%v, in.Final=%v, in.WidthKnown=%v, in.MinWidth=%v\n", isFinal, in.Final, in.WidthKnown, in.MinWidth)
|
||||
// fmt.Printf("PLUGH: isFinal=%v, in.Final=%v, in.WidthKnown=%v, in.MinWidth=%v\n", isFinal, in.Final, in.WidthKnown, in.MinWidth)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -140,7 +141,9 @@ func (a *Assembler) passInst(in *inst.I, setWidth, final bool) (isFinal bool, er
|
||||
addr, _ := a.Flavor.GetAddr()
|
||||
a.Flavor.SetAddr(addr + in.MinWidth)
|
||||
} else {
|
||||
a.Flavor.ClearAddr()
|
||||
if a.Flavor.AddrKnown() {
|
||||
a.Flavor.ClearAddr(in.Sprintf("lost known address"))
|
||||
}
|
||||
}
|
||||
|
||||
return isFinal, nil
|
||||
@ -150,7 +153,7 @@ func (a *Assembler) passInst(in *inst.I, setWidth, final bool) (isFinal bool, er
|
||||
// instructions to set their final width. If final is true, it returns
|
||||
// an error for any instruction that cannot be finalized.
|
||||
func (a *Assembler) Pass(setWidth, final bool) (isFinal bool, err error) {
|
||||
fmt.Printf("PLUGH: Pass(%v, %v): %d instructions\n", setWidth, final, len(a.Insts))
|
||||
// fmt.Printf("PLUGH: Pass(%v, %v): %d instructions\n", setWidth, final, len(a.Insts))
|
||||
setWidth = setWidth || final // final ⊢ setWidth
|
||||
|
||||
a.initPass()
|
||||
@ -162,9 +165,9 @@ func (a *Assembler) Pass(setWidth, final bool) (isFinal bool, err error) {
|
||||
return false, err
|
||||
}
|
||||
if final && !instFinal {
|
||||
return false, fmt.Errorf("Cannot finalize instruction: %s", in)
|
||||
return false, in.Errorf("cannot finalize instruction: %s", in)
|
||||
}
|
||||
fmt.Printf("PLUGH: instFinal=%v, in.Final=%v, in.WidthKnown=%v, in.MinWidth=%v\n", instFinal, in.Final, in.WidthKnown, in.MinWidth)
|
||||
// fmt.Printf("PLUGH: instFinal=%v, in.Final=%v, in.WidthKnown=%v, in.MinWidth=%v\n", instFinal, in.Final, in.WidthKnown, in.MinWidth)
|
||||
isFinal = isFinal && instFinal
|
||||
}
|
||||
|
||||
@ -177,7 +180,7 @@ func (a *Assembler) RawBytes() ([]byte, error) {
|
||||
result := []byte{}
|
||||
for _, in := range a.Insts {
|
||||
if !in.Final {
|
||||
return []byte{}, fmt.Errorf("cannot finalize value: %s", in)
|
||||
return []byte{}, in.Errorf("cannot finalize value: %s", in)
|
||||
}
|
||||
result = append(result, in.Data...)
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ type Context interface {
|
||||
Set(name string, value uint16)
|
||||
Get(name string) (uint16, bool)
|
||||
SetAddr(uint16)
|
||||
ClearAddr()
|
||||
ClearAddr(message string)
|
||||
ClearMesg() string
|
||||
GetAddr() (uint16, bool)
|
||||
Zero() (uint16, error) // type ZeroFunc
|
||||
RemoveChanged()
|
||||
@ -20,6 +21,7 @@ type SimpleContext struct {
|
||||
symbols map[string]symbolValue
|
||||
addr int32
|
||||
lastLabel string
|
||||
clearMesg string // Saved message describing why Addr was cleared.
|
||||
}
|
||||
|
||||
type symbolValue struct {
|
||||
@ -46,14 +48,19 @@ func (sc *SimpleContext) Get(name string) (uint16, bool) {
|
||||
return s.v, found
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) ClearAddr() {
|
||||
func (sc *SimpleContext) ClearAddr(message string) {
|
||||
sc.addr = -1
|
||||
sc.clearMesg = message
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) SetAddr(addr uint16) {
|
||||
sc.addr = int32(addr)
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) ClearMesg() string {
|
||||
return sc.clearMesg
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) GetAddr() (uint16, bool) {
|
||||
if sc.addr == -1 {
|
||||
return 0, false
|
||||
|
@ -8,6 +8,8 @@ additional flavors should be straightforward.
|
||||
|
||||
TODO(zellyn): make errors return line and character position.
|
||||
TODO(zellyn): scma requires .EQ and .BS to have known values. Is this universal?
|
||||
TODO(zellyn): make lineparse have a line, rather than the reverse.
|
||||
TODO(zellyn): implement ca65 and compile ehbasic
|
||||
|
||||
*/
|
||||
package asm
|
||||
|
@ -5,14 +5,15 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zellyn/go6502/asm/context"
|
||||
"github.com/zellyn/go6502/asm/lines"
|
||||
)
|
||||
|
||||
type UnknownLabelError struct {
|
||||
Label string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e UnknownLabelError) Error() string {
|
||||
return fmt.Sprintf(`unknown label: "%s"`, e.Label)
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
type Operator int
|
||||
@ -99,7 +100,7 @@ func (e *E) Width() uint16 {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (e *E) Eval(ctx context.Context) (uint16, error) {
|
||||
func (e *E) Eval(ctx context.Context, ln *lines.Line) (uint16, error) {
|
||||
if e == nil {
|
||||
return 0, errors.New("cannot Eval() nil expression")
|
||||
}
|
||||
@ -111,22 +112,27 @@ func (e *E) Eval(ctx context.Context) (uint16, error) {
|
||||
if val, ok := ctx.Get(e.Text); ok {
|
||||
return val, nil
|
||||
}
|
||||
return 0, UnknownLabelError{Label: e.Text}
|
||||
if e.Text == "*" && !ctx.AddrKnown() {
|
||||
e := ln.Errorf("address unknown due to %v", ctx.ClearMesg())
|
||||
return 0, UnknownLabelError{Err: e}
|
||||
|
||||
}
|
||||
return 0, UnknownLabelError{Err: ln.Errorf("unknown label: %s", e.Text)}
|
||||
case OpMinus:
|
||||
l, err := e.Left.Eval(ctx)
|
||||
l, err := e.Left.Eval(ctx, ln)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if e.Right == nil {
|
||||
return -l, nil
|
||||
}
|
||||
r, err := e.Right.Eval(ctx)
|
||||
r, err := e.Right.Eval(ctx, ln)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return l - r, nil
|
||||
case OpMsb, OpLsb:
|
||||
l, err := e.Left.Eval(ctx)
|
||||
l, err := e.Left.Eval(ctx, ln)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -137,11 +143,11 @@ func (e *E) Eval(ctx context.Context) (uint16, error) {
|
||||
case OpByte:
|
||||
return e.Val, nil
|
||||
case OpPlus, OpMul, OpDiv, OpLt, OpGt, OpEq:
|
||||
l, err := e.Left.Eval(ctx)
|
||||
l, err := e.Left.Eval(ctx, ln)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r, err := e.Right.Eval(ctx)
|
||||
r, err := e.Right.Eval(ctx, ln)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -177,8 +183,8 @@ func (e *E) Eval(ctx context.Context) (uint16, error) {
|
||||
}
|
||||
|
||||
// CheckedEval calls Eval, but also turns UnknownLabelErrors into labelMissing booleans.
|
||||
func (e *E) CheckedEval(ctx context.Context) (val uint16, labelMissing bool, err error) {
|
||||
val, err = e.Eval(ctx)
|
||||
func (e *E) CheckedEval(ctx context.Context, ln *lines.Line) (val uint16, labelMissing bool, err error) {
|
||||
val, err = e.Eval(ctx, ln)
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
return val, false, nil
|
||||
@ -189,21 +195,21 @@ func (e *E) CheckedEval(ctx context.Context) (val uint16, labelMissing bool, err
|
||||
}
|
||||
|
||||
// FixLabels attempts to turn .1 into LAST_LABEL.1
|
||||
func (e *E) FixLabels(last string) error {
|
||||
func (e *E) FixLabels(last string, ln *lines.Line) error {
|
||||
if e.Text != "" && e.Text[0] == '.' {
|
||||
if last == "" {
|
||||
return fmt.Errorf("Reference to sub-label '%s' before full label.", e.Text)
|
||||
return ln.Errorf("reference to sub-label '%s' before full label.", e.Text)
|
||||
}
|
||||
e.Text = last + e.Text
|
||||
e.Text = last + "/" + e.Text
|
||||
}
|
||||
|
||||
if e.Left != nil {
|
||||
if err := e.Left.FixLabels(last); err != nil {
|
||||
if err := e.Left.FixLabels(last, ln); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if e.Right != nil {
|
||||
return e.Right.FixLabels(last)
|
||||
return e.Right.FixLabels(last, ln)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -31,3 +31,7 @@ func (a *AS65) Zero() (uint16, error) {
|
||||
func (a *AS65) DefaultOrigin() (uint16, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (a *AS65) SetWidthsOnFirstPass() bool {
|
||||
return false
|
||||
}
|
||||
|
@ -32,6 +32,13 @@ func TestMultiline(t *testing.T) {
|
||||
"L2 NOP",
|
||||
}, nil, "ad0300ea", false},
|
||||
|
||||
// sc-asm sets instruction widths on the first pass
|
||||
{ss, "Later label: wide", []string{
|
||||
" LDA FOO",
|
||||
"FOO .EQ $FF",
|
||||
" NOP",
|
||||
}, nil, "adff00ea", true},
|
||||
|
||||
// Sub-labels
|
||||
{ss, "Sublabels", []string{
|
||||
"L1 BEQ .1",
|
||||
|
@ -32,3 +32,7 @@ func (a *Merlin) Zero() (uint16, error) {
|
||||
func (a *Merlin) DefaultOrigin() (uint16, error) {
|
||||
return 0x8000, nil
|
||||
}
|
||||
|
||||
func (a *Merlin) SetWidthsOnFirstPass() bool {
|
||||
panic("don't know yet")
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ const cmdChars = letters + "."
|
||||
const fileChars = letters + digits + "."
|
||||
const operatorChars = "+-*/<>="
|
||||
|
||||
// 40 spaces = comment column
|
||||
const comment_whitespace_prefix = " "
|
||||
|
||||
type directiveInfo struct {
|
||||
Type inst.Type
|
||||
Func func(inst.I, *lines.Parse) (inst.I, error)
|
||||
@ -82,18 +85,24 @@ func (a *SCMA) ParseInstr(line lines.Line) (inst.I, error) {
|
||||
if lp.AcceptRun(digits) {
|
||||
s := lp.Emit()
|
||||
if len(s) != 4 {
|
||||
return inst.I{}, fmt.Errorf("Line number must be exactly 4 digits: %s", s)
|
||||
return inst.I{}, line.Errorf("line number must be exactly 4 digits: %s", s)
|
||||
}
|
||||
if !lp.Consume(" ") && lp.Peek() != lines.Eol {
|
||||
return inst.I{}, fmt.Errorf("Line number (%s) followed by non-space", s)
|
||||
return inst.I{}, line.Errorf("line number (%s) followed by non-space", s)
|
||||
}
|
||||
i, err := strconv.ParseUint(s, 10, 16)
|
||||
if err != nil {
|
||||
return inst.I{}, fmt.Errorf("Invalid line number: %s: %s", s, err)
|
||||
return inst.I{}, line.Errorf("invalid line number: %s: %s", s, err)
|
||||
}
|
||||
in.DeclaredLine = uint16(i)
|
||||
}
|
||||
|
||||
// Comment by virtue of long whitespace prefix
|
||||
if strings.HasPrefix(lp.Rest(), comment_whitespace_prefix) {
|
||||
in.Type = inst.TypeNone
|
||||
return in, nil
|
||||
}
|
||||
|
||||
// Empty line or comment
|
||||
trimmed := strings.TrimSpace(lp.Rest())
|
||||
if trimmed == "" || trimmed[0] == '*' {
|
||||
@ -124,6 +133,10 @@ func (a *SCMA) DefaultOrigin() (uint16, error) {
|
||||
return 0x0800, nil
|
||||
}
|
||||
|
||||
func (a *SCMA) SetWidthsOnFirstPass() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// parseCmd parses the "command" part of an instruction: we expect to be
|
||||
// looking at a non-whitespace character.
|
||||
func (a *SCMA) parseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
@ -132,7 +145,7 @@ func (a *SCMA) parseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
}
|
||||
if !lp.AcceptRun(cmdChars) {
|
||||
c := lp.Next()
|
||||
return inst.I{}, fmt.Errorf("Expecting instruction, found '%c' (%d)", c, c)
|
||||
return inst.I{}, in.Errorf("expecting instruction, found '%c' (%d)", c, c)
|
||||
}
|
||||
in.Command = lp.Emit()
|
||||
if dir, ok := a.directives[in.Command]; ok {
|
||||
@ -147,7 +160,7 @@ func (a *SCMA) parseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
in.Type = inst.TypeOp
|
||||
return a.parseOpArgs(in, lp, summary)
|
||||
}
|
||||
return inst.I{}, fmt.Errorf(`Not implemented: "%s": `, in.Command, in.Line)
|
||||
return inst.I{}, in.Errorf(`not implemented: "%s": `, in.Command, in.Line)
|
||||
}
|
||||
|
||||
// parseMacroCall parses a macro call. We expect to be looking at a the
|
||||
@ -156,14 +169,14 @@ func (a *SCMA) parseMacroCall(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
in.Type = inst.TypeMacroCall
|
||||
if !lp.AcceptRun(cmdChars) {
|
||||
c := lp.Next()
|
||||
return inst.I{}, fmt.Errorf("Expecting macro name, found '%c' (%d)", c, c)
|
||||
return inst.I{}, in.Errorf("expecting macro name, found '%c' (%d)", c, c)
|
||||
}
|
||||
in.Command = lp.Emit()
|
||||
|
||||
lp.Consume(whitespace)
|
||||
|
||||
for {
|
||||
s, err := a.parseMacroArg(lp)
|
||||
s, err := a.parseMacroArg(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
}
|
||||
@ -178,9 +191,9 @@ func (a *SCMA) parseMacroCall(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
|
||||
// parseMacroArg parses a single macro argument. We expect to be looking at the first
|
||||
// character of a macro argument.
|
||||
func (a *SCMA) parseMacroArg(lp *lines.Parse) (string, error) {
|
||||
func (a *SCMA) parseMacroArg(in inst.I, lp *lines.Parse) (string, error) {
|
||||
if lp.Peek() == '"' {
|
||||
return a.parseQuoted(lp)
|
||||
return a.parseQuoted(in, lp)
|
||||
}
|
||||
lp.AcceptUntil(whitespace + ",")
|
||||
return lp.Emit(), nil
|
||||
@ -188,7 +201,7 @@ func (a *SCMA) parseMacroArg(lp *lines.Parse) (string, error) {
|
||||
|
||||
// parseQuoted parses a single quoted string macro argument. We expect
|
||||
// to be looking at the first quote.
|
||||
func (a *SCMA) parseQuoted(lp *lines.Parse) (string, error) {
|
||||
func (a *SCMA) parseQuoted(in inst.I, lp *lines.Parse) (string, error) {
|
||||
if !lp.Consume(`"`) {
|
||||
panic(fmt.Sprintf("parseQuoted called not looking at a quote"))
|
||||
}
|
||||
@ -202,12 +215,12 @@ func (a *SCMA) parseQuoted(lp *lines.Parse) (string, error) {
|
||||
s := lp.Emit()
|
||||
if !lp.Consume(`"`) {
|
||||
c := lp.Peek()
|
||||
return "", fmt.Errorf("Expected closing quote; got %s", c)
|
||||
return "", in.Errorf("Expected closing quote; got %s", c)
|
||||
}
|
||||
|
||||
c := lp.Peek()
|
||||
if c != ',' && c != ' ' && c != lines.Eol && c != '\t' {
|
||||
return "", fmt.Errorf("Unexpected char after quoted string: '%s'", c)
|
||||
return "", in.Errorf("Unexpected char after quoted string: '%s'", c)
|
||||
}
|
||||
|
||||
return strings.Replace(s, `""`, `"`, -1), nil
|
||||
@ -234,7 +247,7 @@ func (a *SCMA) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
|
||||
lp.Consume(whitespace)
|
||||
if lp.Consume(whitespace) || lp.Peek() == lines.Eol {
|
||||
if !summary.AnyModes(opcodes.MODE_A) {
|
||||
return i, fmt.Errorf("%s with no arguments", in.Command)
|
||||
return i, in.Errorf("%s with no arguments", in.Command)
|
||||
}
|
||||
op, ok := summary.OpForMode(opcodes.MODE_A)
|
||||
if !ok {
|
||||
@ -251,10 +264,10 @@ func (a *SCMA) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
|
||||
|
||||
indirect := lp.Consume("(")
|
||||
if indirect && !summary.AnyModes(opcodes.MODE_INDIRECT_ANY) {
|
||||
return i, fmt.Errorf("%s doesn't support any indirect modes", in.Command)
|
||||
return i, in.Errorf("%s doesn't support any indirect modes", in.Command)
|
||||
}
|
||||
xy := '-'
|
||||
expr, err := a.parseExpression(lp)
|
||||
expr, err := a.parseExpression(in, lp)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
@ -265,25 +278,25 @@ func (a *SCMA) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
|
||||
xy = 'x'
|
||||
} else if lp.Consume("yY") {
|
||||
if indirect {
|
||||
return i, fmt.Errorf(",Y unexpected inside parens")
|
||||
return i, in.Errorf(",Y unexpected inside parens")
|
||||
}
|
||||
xy = 'y'
|
||||
} else {
|
||||
return i, fmt.Errorf("X or Y expected after comma")
|
||||
return i, in.Errorf("X or Y expected after comma")
|
||||
}
|
||||
}
|
||||
comma2 := false
|
||||
if indirect {
|
||||
if !lp.Consume(")") {
|
||||
return i, fmt.Errorf("Expected closing paren")
|
||||
return i, in.Errorf("Expected closing paren")
|
||||
}
|
||||
comma2 = lp.Consume(",")
|
||||
if comma2 {
|
||||
if comma {
|
||||
return i, fmt.Errorf("Cannot have ,X or ,Y twice.")
|
||||
return i, in.Errorf("Cannot have ,X or ,Y twice.")
|
||||
}
|
||||
if !lp.Consume("yY") {
|
||||
return i, fmt.Errorf("Only ,Y can follow parens.")
|
||||
return i, in.Errorf("Only ,Y can follow parens.")
|
||||
}
|
||||
xy = 'y'
|
||||
}
|
||||
@ -294,7 +307,7 @@ func (a *SCMA) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
|
||||
|
||||
func (a *SCMA) parseAddress(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
expr, err := a.parseExpression(lp)
|
||||
expr, err := a.parseExpression(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
}
|
||||
@ -317,13 +330,13 @@ func (a *SCMA) parseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
}
|
||||
delim := lp.Next()
|
||||
if delim == lines.Eol || strings.IndexRune(whitespace, delim) >= 0 {
|
||||
return inst.I{}, fmt.Errorf("%s expects delimeter, found '%s'", in.Command, delim)
|
||||
return inst.I{}, in.Errorf("%s expects delimeter, found '%s'", in.Command, delim)
|
||||
}
|
||||
lp.Ignore()
|
||||
lp.AcceptUntil(string(delim))
|
||||
delim2 := lp.Next()
|
||||
if delim != delim2 {
|
||||
return inst.I{}, fmt.Errorf("%s: expected closing delimeter '%s'; got '%s'", in.Command, delim, delim2)
|
||||
return inst.I{}, in.Errorf("%s: expected closing delimeter '%s'; got '%s'", in.Command, delim, delim2)
|
||||
}
|
||||
lp.Backup()
|
||||
in.Data = []byte(lp.Emit())
|
||||
@ -338,7 +351,7 @@ func (a *SCMA) parseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
|
||||
func (a *SCMA) parseBlockStorage(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
ex, err := a.parseExpression(lp)
|
||||
ex, err := a.parseExpression(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
}
|
||||
@ -349,7 +362,7 @@ func (a *SCMA) parseBlockStorage(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
func (a *SCMA) parseData(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
for {
|
||||
ex, err := a.parseExpression(lp)
|
||||
ex, err := a.parseExpression(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
}
|
||||
@ -363,7 +376,7 @@ func (a *SCMA) parseData(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
|
||||
func (a *SCMA) parseDo(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
expr, err := a.parseExpression(lp)
|
||||
expr, err := a.parseExpression(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
}
|
||||
@ -377,7 +390,7 @@ func (a *SCMA) parseDo(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
|
||||
func (a *SCMA) parseEquate(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
expr, err := a.parseExpression(lp)
|
||||
expr, err := a.parseExpression(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
}
|
||||
@ -392,15 +405,15 @@ func (a *SCMA) parseEquate(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
func (a *SCMA) parseHexString(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
if !lp.AcceptRun(hexdigits) {
|
||||
return inst.I{}, fmt.Errorf("%s expects hex digits; got '%s'", in.Command, lp.Next())
|
||||
return inst.I{}, in.Errorf("%s expects hex digits; got '%s'", in.Command, lp.Next())
|
||||
}
|
||||
hs := lp.Emit()
|
||||
if len(hs)%2 != 0 {
|
||||
return inst.I{}, fmt.Errorf("%s expects pairs of hex digits; got %d", in.Command, len(hs))
|
||||
return inst.I{}, in.Errorf("%s expects pairs of hex digits; got %d", in.Command, len(hs))
|
||||
}
|
||||
var err error
|
||||
if in.Data, err = hex.DecodeString(hs); err != nil {
|
||||
return inst.I{}, fmt.Errorf("%s: error decoding hex string: %s", in.Command, err)
|
||||
return inst.I{}, in.Errorf("%s: error decoding hex string: %s", in.Command, err)
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
@ -408,7 +421,7 @@ func (a *SCMA) parseHexString(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
func (a *SCMA) parseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
if !lp.AcceptRun(fileChars) {
|
||||
return inst.I{}, fmt.Errorf("Expecting filename, found '%c'", lp.Next())
|
||||
return inst.I{}, in.Errorf("Expecting filename, found '%c'", lp.Next())
|
||||
}
|
||||
in.TextArg = lp.Emit()
|
||||
in.WidthKnown = true
|
||||
@ -427,10 +440,10 @@ func (a *SCMA) parseNoArgDir(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
}
|
||||
|
||||
func (a *SCMA) parseNotImplemented(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
return inst.I{}, fmt.Errorf("Not implemented (yet?): %s", in.Command)
|
||||
return inst.I{}, in.Errorf("not implemented (yet?): %s", in.Command)
|
||||
}
|
||||
|
||||
func (a *SCMA) parseExpression(lp *lines.Parse) (*expr.E, error) {
|
||||
func (a *SCMA) parseExpression(in inst.I, lp *lines.Parse) (*expr.E, error) {
|
||||
var outer *expr.E
|
||||
if lp.Accept("#/") {
|
||||
switch lp.Emit() {
|
||||
@ -441,14 +454,14 @@ func (a *SCMA) parseExpression(lp *lines.Parse) (*expr.E, error) {
|
||||
}
|
||||
}
|
||||
|
||||
tree, err := a.parseTerm(lp)
|
||||
tree, err := a.parseTerm(in, lp)
|
||||
if err != nil {
|
||||
return &expr.E{}, err
|
||||
}
|
||||
|
||||
for lp.Accept(operatorChars) {
|
||||
c := lp.Emit()
|
||||
right, err := a.parseTerm(lp)
|
||||
right, err := a.parseTerm(in, lp)
|
||||
if err != nil {
|
||||
return &expr.E{}, err
|
||||
}
|
||||
@ -462,7 +475,7 @@ func (a *SCMA) parseExpression(lp *lines.Parse) (*expr.E, error) {
|
||||
return tree, nil
|
||||
}
|
||||
|
||||
func (a *SCMA) parseTerm(lp *lines.Parse) (*expr.E, error) {
|
||||
func (a *SCMA) parseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) {
|
||||
ex := &expr.E{}
|
||||
top := ex
|
||||
|
||||
@ -482,12 +495,12 @@ func (a *SCMA) parseTerm(lp *lines.Parse) (*expr.E, error) {
|
||||
if lp.Consume("$") {
|
||||
if !lp.AcceptRun(hexdigits) {
|
||||
c := lp.Next()
|
||||
return &expr.E{}, fmt.Errorf("Expecting hex number, found '%c' (%d)", c, c)
|
||||
return &expr.E{}, in.Errorf("expecting hex number, found '%c' (%d)", c, c)
|
||||
}
|
||||
s := lp.Emit()
|
||||
i, err := strconv.ParseUint(s, 16, 16)
|
||||
if err != nil {
|
||||
return &expr.E{}, fmt.Errorf("Invalid hex number: %s: %s", s, err)
|
||||
return &expr.E{}, in.Errorf("invalid hex number: %s: %s", s, err)
|
||||
}
|
||||
ex.Op = expr.OpLeaf
|
||||
ex.Val = uint16(i)
|
||||
@ -499,17 +512,29 @@ func (a *SCMA) parseTerm(lp *lines.Parse) (*expr.E, error) {
|
||||
s := lp.Emit()
|
||||
i, err := strconv.ParseUint(s, 10, 16)
|
||||
if err != nil {
|
||||
return &expr.E{}, fmt.Errorf("Invalid number: %s: %s", s, err)
|
||||
return &expr.E{}, in.Errorf("invalid number: %s: %s", s, err)
|
||||
}
|
||||
ex.Op = expr.OpLeaf
|
||||
ex.Val = uint16(i)
|
||||
return top, nil
|
||||
}
|
||||
|
||||
// Character
|
||||
if lp.Consume("'") {
|
||||
c := lp.Next()
|
||||
if c == lines.Eol {
|
||||
return &expr.E{}, in.Errorf("end of line after quote")
|
||||
}
|
||||
ex.Op = expr.OpLeaf
|
||||
ex.Val = uint16(c)
|
||||
lp.Ignore()
|
||||
return top, nil
|
||||
}
|
||||
|
||||
// Label
|
||||
if !lp.AcceptRun(labelChars) {
|
||||
c := lp.Next()
|
||||
return &expr.E{}, fmt.Errorf("Expecting *, (hex) number, or label; found '%c' (%d)", c, c)
|
||||
return &expr.E{}, in.Errorf("expecting *, (hex) number, or label; found '%c' (%d)", c, c)
|
||||
}
|
||||
|
||||
ex.Op = expr.OpLeaf
|
||||
|
@ -26,6 +26,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
|
||||
{ss, "* Comment", "{-}", ""},
|
||||
{aa, "; Comment", "{-}", ""},
|
||||
{mm, "* Comment", "{-}", ""},
|
||||
{ss, " far-out-comment", "{-}", ""},
|
||||
{ss, "Label", "{- 'Label'}", ""},
|
||||
{aa, "Label", "{- 'Label'}", ""},
|
||||
{mm, "Label", "{- 'Label'}", ""},
|
||||
@ -118,6 +119,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
|
||||
{ss, " .EN", "{end}", ""},
|
||||
{ss, `>SAM AB,$12,"A B","A, B, "" C"`,
|
||||
`{call SAM {"AB", "$12", "A B", "A, B, \" C"}}`, ""},
|
||||
{ss, " LDX #']+$80", "{LDX/imm (lsb (+ $005d $0080))}", "a2dd"},
|
||||
}
|
||||
|
||||
// TODO(zellyn): Add tests for finalization of four SCMA directives:
|
||||
|
@ -182,13 +182,13 @@ func (i *I) Compute(c context.Context, setWidth bool, final bool) (bool, error)
|
||||
func (i *I) FixLabels(last string) error {
|
||||
if i.Label != "" && i.Label[0] == '.' {
|
||||
if last == "" {
|
||||
return fmt.Errorf("Reference to sub-label '%s' before full label.", i.Label)
|
||||
return i.Errorf("Reference to sub-label '%s' before full label.", i.Label)
|
||||
}
|
||||
i.Label = last + i.Label
|
||||
i.Label = last + "/" + i.Label
|
||||
}
|
||||
|
||||
for _, e := range i.Exprs {
|
||||
if err := e.FixLabels(last); err != nil {
|
||||
if err := e.FixLabels(last, i.Line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -213,7 +213,7 @@ func (i *I) computeLabel(c context.Context, setWidth bool, final bool) error {
|
||||
|
||||
lval, lok := c.Get(i.Label)
|
||||
if lok && addr != lval {
|
||||
return fmt.Errorf("Trying to set label '%s' to $%04x, but it already has value $%04x", i.Label, addr, lval)
|
||||
return i.Errorf("Trying to set label '%s' to $%04x, but it already has value $%04x", i.Label, addr, lval)
|
||||
}
|
||||
c.Set(i.Label, addr)
|
||||
return nil
|
||||
@ -234,7 +234,7 @@ func (i *I) computeData(c context.Context, setWidth bool, final bool) (bool, err
|
||||
for _, e := range i.Exprs {
|
||||
w := e.Width()
|
||||
width += w
|
||||
val, labelMissing, err := e.CheckedEval(c)
|
||||
val, labelMissing, err := e.CheckedEval(c, i.Line)
|
||||
if err != nil && !labelMissing {
|
||||
return false, err
|
||||
}
|
||||
@ -263,7 +263,7 @@ func (i *I) computeData(c context.Context, setWidth bool, final bool) (bool, err
|
||||
}
|
||||
|
||||
func (i *I) computeBlock(c context.Context, setWidth bool, final bool) (bool, error) {
|
||||
val, err := i.Exprs[0].Eval(c)
|
||||
val, err := i.Exprs[0].Eval(c, i.Line)
|
||||
if err == nil {
|
||||
i.Value = val
|
||||
i.WidthKnown = true
|
||||
@ -272,7 +272,7 @@ func (i *I) computeBlock(c context.Context, setWidth bool, final bool) (bool, er
|
||||
i.MaxWidth = val
|
||||
} else {
|
||||
if setWidth || final {
|
||||
return false, fmt.Errorf("block storage with unknown size")
|
||||
return false, i.Errorf("block storage with unknown size")
|
||||
}
|
||||
}
|
||||
return i.Final, nil
|
||||
@ -283,7 +283,7 @@ func (i *I) computeMustKnow(c context.Context, setWidth bool, final bool) (bool,
|
||||
i.MinWidth = 0
|
||||
i.MaxWidth = 0
|
||||
i.Final = true
|
||||
val, err := i.Exprs[0].Eval(c)
|
||||
val, err := i.Exprs[0].Eval(c, i.Line)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -313,7 +313,7 @@ func (i *I) computeOp(c context.Context, setWidth bool, final bool) (bool, error
|
||||
if len(i.Exprs) == 0 {
|
||||
panic(fmt.Sprintf("Reached computeOp for '%s' with no expressions", i.Command))
|
||||
}
|
||||
val, labelMissing, err := i.Exprs[0].CheckedEval(c)
|
||||
val, labelMissing, err := i.Exprs[0].CheckedEval(c, i.Line)
|
||||
// A real error
|
||||
if !labelMissing && err != nil {
|
||||
return false, err
|
||||
@ -372,17 +372,17 @@ func (i *I) computeOp(c context.Context, setWidth bool, final bool) (bool, error
|
||||
curr, ok := c.GetAddr()
|
||||
if !ok {
|
||||
if final {
|
||||
return false, fmt.Errorf("Cannot determine current address for '%s'", i.Command)
|
||||
return false, i.Errorf("cannot determine current address for '%s'", i.Command)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
// Found both current and target addresses
|
||||
offset := int32(val) - (int32(curr) + 2)
|
||||
if offset > 127 {
|
||||
return false, fmt.Errorf("%s cannot jump forward %d (max 127)", i.Command, offset)
|
||||
return false, i.Errorf("%s cannot jump forward %d (max 127)", i.Command, offset)
|
||||
}
|
||||
if offset < -128 {
|
||||
return false, fmt.Errorf("%s cannot jump back %d (max -128)", i.Command, offset)
|
||||
return false, i.Errorf("%s cannot jump back %d (max -128)", i.Command, offset)
|
||||
}
|
||||
val = uint16(offset)
|
||||
}
|
||||
@ -403,3 +403,11 @@ func (i *I) computeOp(c context.Context, setWidth bool, final bool) (bool, error
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (i I) Errorf(format string, a ...interface{}) error {
|
||||
return i.Line.Errorf(format, a...)
|
||||
}
|
||||
|
||||
func (i I) Sprintf(format string, a ...interface{}) string {
|
||||
return i.Line.Sprintf(format, a...)
|
||||
}
|
||||
|
@ -41,3 +41,19 @@ func (l Line) Text() string {
|
||||
func (l Line) String() string {
|
||||
return fmt.Sprintf("%s:%d: %s", l.Context.Filename, l.LineNo, l.Parse.Text())
|
||||
}
|
||||
|
||||
func (l Line) Errorf(format string, a ...interface{}) error {
|
||||
filename := "unknown file"
|
||||
if l.Context != nil {
|
||||
filename = l.Context.Filename
|
||||
}
|
||||
return fmt.Errorf(fmt.Sprintf("%s:%d: %s", filename, l.LineNo, format), a...)
|
||||
}
|
||||
|
||||
func (l Line) Sprintf(format string, a ...interface{}) string {
|
||||
filename := "unknown file"
|
||||
if l.Context != nil {
|
||||
filename = l.Context.Filename
|
||||
}
|
||||
return fmt.Sprintf(fmt.Sprintf("%s:%d: %s", filename, l.LineNo, format), a...)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user