1
0
mirror of https://github.com/zellyn/go6502.git synced 2024-12-29 01:33:45 +00:00

Moving more stuff to first pass.

This commit is contained in:
Zellyn Hunter 2014-08-29 16:34:52 -07:00
parent cab917364e
commit c07a3732a6
12 changed files with 160 additions and 107 deletions

View File

@ -32,6 +32,11 @@ func NewAssembler(flavor flavors.F, opener lines.Opener) *Assembler {
} }
} }
type ifdef struct {
active bool
in inst.I
}
// Load loads a new assembler file, deleting any previous data. // Load loads a new assembler file, deleting any previous data.
func (a *Assembler) Load(filename string, prefix int) error { func (a *Assembler) Load(filename string, prefix int) error {
a.initPass() a.initPass()
@ -41,7 +46,7 @@ func (a *Assembler) Load(filename string, prefix int) error {
return err return err
} }
lineSources := []lines.LineSource{ls} lineSources := []lines.LineSource{ls}
ifdefs := []bool{} ifdefs := []ifdef{}
macroCall := 0 macroCall := 0
for len(lineSources) > 0 { for len(lineSources) > 0 {
line, done, err := lineSources[0].Next() line, done, err := lineSources[0].Next()
@ -52,14 +57,18 @@ func (a *Assembler) Load(filename string, prefix int) error {
lineSources = lineSources[1:] lineSources = lineSources[1:]
continue continue
} }
in, parseErr := a.Flavor.ParseInstr(a.Ctx, line, false) inactive := len(ifdefs) > 0 && !ifdefs[0].active
if len(ifdefs) > 0 && !ifdefs[0] && in.Type != inst.TypeIfdefElse && in.Type != inst.TypeIfdefEnd { mode := flavors.ParseModeNormal
// we're in an inactive ifdef branch if inactive {
mode = flavors.ParseModeInactive
}
in, parseErr := a.Flavor.ParseInstr(a.Ctx, line, mode)
if inactive && in.Type != inst.TypeIfdefElse && in.Type != inst.TypeIfdefEnd {
// we're still in an inactive ifdef branch
continue continue
} }
if parseErr != nil {
if err != nil { return parseErr
return err
} }
if _, err := a.passInst(&in, false); err != nil { if _, err := a.passInst(&in, false); err != nil {
@ -102,13 +111,13 @@ func (a *Assembler) Load(filename string, prefix int) error {
if err != nil { if err != nil {
return in.Errorf("cannot eval ifdef condition: %v", err) return in.Errorf("cannot eval ifdef condition: %v", err)
} }
ifdefs = append([]bool{val != 0}, ifdefs...) ifdefs = append([]ifdef{{val != 0, in}}, ifdefs...)
case inst.TypeIfdefElse: case inst.TypeIfdefElse:
if len(ifdefs) == 0 { if len(ifdefs) == 0 {
return in.Errorf("ifdef else branch encountered outside ifdef: %s", line) return in.Errorf("ifdef else branch encountered outside ifdef: %s", line)
} }
ifdefs[0] = !ifdefs[0] ifdefs[0].active = !ifdefs[0].active
case inst.TypeIfdefEnd: case inst.TypeIfdefEnd:
if len(ifdefs) == 0 { if len(ifdefs) == 0 {
return in.Errorf("ifdef end encountered outside ifdef: %s", line) return in.Errorf("ifdef end encountered outside ifdef: %s", line)
@ -132,6 +141,9 @@ func (a *Assembler) Load(filename string, prefix int) error {
} }
a.Insts = append(a.Insts, &in) a.Insts = append(a.Insts, &in)
} }
if len(ifdefs) > 0 {
return ifdefs[0].in.Errorf("Ifdef not closed before end of file")
}
return nil return nil
} }
@ -168,7 +180,7 @@ 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(a.Ctx, line, true) in2, err := a.Flavor.ParseInstr(a.Ctx, line, flavors.ParseModeMacroSave)
if a.Flavor.LocalMacroLabels() && in2.Label != "" { if a.Flavor.LocalMacroLabels() && in2.Label != "" {
m.Locals[in2.Label] = true m.Locals[in2.Label] = true
} }
@ -193,13 +205,17 @@ 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) {
if in.Type == inst.TypeOrg {
a.Ctx.SetAddr(in.Addr)
return true, nil
}
isFinal, err = in.Compute(a.Ctx, final) isFinal, err = in.Compute(a.Ctx, final)
if err != nil { if err != nil {
return false, err return false, err
} }
// Update address // Update address
addr, _ := a.Ctx.GetAddr() addr := a.Ctx.GetAddr()
in.Addr = addr in.Addr = addr
a.Ctx.SetAddr(addr + in.Width) a.Ctx.SetAddr(addr + in.Width)

View File

@ -133,8 +133,8 @@ http://twimgs.com/informationweek/byte/archive/Apple-II-Description/The-Apple-II
It's a bit tricky: It's a bit tricky:
1. Parse instruction. 1. Parse instruction.
1. Set the org/target immediately, updating current address. 1. Set the current label to the current address, unless it's an equate
2. Set the current label. 2. Set the org/target immediately, updating current address.
3. Force equates to evaluate immediately. 3. Force equates to evaluate immediately.
4. Evaluate instructions. 4. Evaluate instructions.

View File

@ -6,7 +6,7 @@ type Context interface {
Set(name string, value uint16) Set(name string, value uint16)
Get(name string) (uint16, bool) Get(name string) (uint16, bool)
SetAddr(uint16) SetAddr(uint16)
GetAddr() (uint16, bool) GetAddr() uint16
DivZero() *uint16 DivZero() *uint16
SetDivZero(uint16) SetDivZero(uint16)
RemoveChanged() RemoveChanged()
@ -33,7 +33,7 @@ type macroCall struct {
type SimpleContext struct { type SimpleContext struct {
symbols map[string]symbolValue symbols map[string]symbolValue
addr int32 addr uint16
lastLabel string lastLabel string
highbit byte // OR-mask for ASCII high bit highbit byte // OR-mask for ASCII high bit
onOff map[string]bool onOff map[string]bool
@ -56,7 +56,7 @@ func (sc *SimpleContext) fix() {
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(), true
} }
sc.fix() sc.fix()
s, found := sc.symbols[name] s, found := sc.symbols[name]
@ -64,14 +64,11 @@ func (sc *SimpleContext) Get(name string) (uint16, bool) {
} }
func (sc *SimpleContext) SetAddr(addr uint16) { func (sc *SimpleContext) SetAddr(addr uint16) {
sc.addr = int32(addr) sc.addr = addr
} }
func (sc *SimpleContext) GetAddr() (uint16, bool) { func (sc *SimpleContext) GetAddr() uint16 {
if sc.addr == -1 { return sc.addr
return 0, false
}
return uint16(sc.addr), true
} }
func (sc *SimpleContext) Set(name string, value uint16) { func (sc *SimpleContext) Set(name string, value uint16) {

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"github.com/zellyn/go6502/asm/context" "github.com/zellyn/go6502/asm/context"
"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"
) )
@ -18,7 +19,7 @@ func New() *AS65 {
} }
// Parse an entire instruction, or return an appropriate error. // Parse an entire instruction, or return an appropriate error.
func (a *AS65) ParseInstr(ctx context.Context, line lines.Line, quick bool) (inst.I, error) { func (a *AS65) ParseInstr(ctx context.Context, line lines.Line, mode flavors.ParseMode) (inst.I, error) {
return inst.I{}, nil return inst.I{}, nil
} }

View File

@ -170,11 +170,7 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
} }
func RelativeAddr(c context.Context, in inst.I, val uint16) (byte, error) { func RelativeAddr(c context.Context, in inst.I, val uint16) (byte, error) {
curr, ok := c.GetAddr() curr := c.GetAddr()
if !ok {
return 0, in.Errorf("cannot determine current address for '%s'", in.Command)
}
// Found both current and target addresses
offset := int32(val) - (int32(curr) + 2) offset := int32(val) - (int32(curr) + 2)
if offset > 127 { if offset > 127 {
return 0, in.Errorf("%s cannot jump forward %d (max 127) from $%04x to $%04x", in.Command, offset, curr+2, val) return 0, in.Errorf("%s cannot jump forward %d (max 127) from $%04x to $%04x", in.Command, offset, curr+2, val)

View File

@ -6,8 +6,16 @@ import (
"github.com/zellyn/go6502/asm/lines" "github.com/zellyn/go6502/asm/lines"
) )
type ParseMode int
const (
ParseModeNormal ParseMode = iota
ParseModeMacroSave
ParseModeInactive
)
type F interface { type F interface {
ParseInstr(ctx context.Context, Line lines.Line, quick bool) (inst.I, error) ParseInstr(ctx context.Context, Line lines.Line, mode ParseMode) (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

View File

@ -42,7 +42,7 @@ func New() *Merlin {
m.DefaultOriginVal = 0x8000 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.ParseOrg, 0},
"OBJ": {inst.TypeNone, nil, 0}, "OBJ": {inst.TypeNone, nil, 0},
"ENDASM": {inst.TypeEnd, m.ParseNoArgDir, 0}, "ENDASM": {inst.TypeEnd, m.ParseNoArgDir, 0},
"=": {inst.TypeEqu, m.ParseEquate, inst.VarEquNormal}, "=": {inst.TypeEqu, m.ParseEquate, inst.VarEquNormal},
@ -66,6 +66,11 @@ func New() *Merlin {
"PUT": {inst.TypeInclude, m.ParseInclude, 0}, "PUT": {inst.TypeInclude, m.ParseInclude, 0},
"USE": {inst.TypeInclude, m.ParseInclude, 0}, "USE": {inst.TypeInclude, m.ParseInclude, 0},
} }
m.EquateDirectives = map[string]bool{
"=": true,
}
m.Operators = map[string]expr.Operator{ m.Operators = map[string]expr.Operator{
"*": expr.OpMul, "*": expr.OpMul,
"/": expr.OpDiv, "/": expr.OpDiv,

View File

@ -10,6 +10,7 @@ import (
"github.com/zellyn/go6502/asm/context" "github.com/zellyn/go6502/asm/context"
"github.com/zellyn/go6502/asm/expr" "github.com/zellyn/go6502/asm/expr"
"github.com/zellyn/go6502/asm/flavors"
"github.com/zellyn/go6502/asm/flavors/common" "github.com/zellyn/go6502/asm/flavors/common"
"github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/inst"
"github.com/zellyn/go6502/asm/lines" "github.com/zellyn/go6502/asm/lines"
@ -44,6 +45,7 @@ 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
EquateDirectives map[string]bool
LabelChars string LabelChars string
LabelColons Requiredness LabelColons Requiredness
ExplicitARegister Requiredness ExplicitARegister Requiredness
@ -76,7 +78,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(ctx context.Context, line lines.Line, quick bool) (inst.I, error) { func (a *Base) ParseInstr(ctx context.Context, line lines.Line, mode flavors.ParseMode) (inst.I, error) {
lp := line.Parse lp := line.Parse
in := inst.I{Line: &line} in := inst.I{Line: &line}
@ -124,49 +126,87 @@ func (a *Base) ParseInstr(ctx context.Context, line lines.Line, quick bool) (ins
} }
} }
// Handle the label: munge for macros, relative labels, etc.
// If appropriate, set the last parent label.
if !quick {
parent := a.IsNewParentLabel(in.Label)
newL, err := a.FixLabel(ctx, in.Label)
if err != nil {
return in, in.Errorf("%v", err)
}
in.Label = newL
if parent {
ctx.SetLastLabel(in.Label)
}
}
// Ignore whitespace at the start or after the label. // Ignore whitespace at the start or after the label.
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
if lp.Peek() == lines.Eol || lp.Peek() == a.CommentChar { if lp.Peek() == lines.Eol || lp.Peek() == a.CommentChar {
in.Type = inst.TypeNone in.Type = inst.TypeNone
if mode == flavors.ParseModeNormal {
if err := a.handleLabel(ctx, in); err != nil {
return in, err
}
}
return in, nil return in, nil
} }
return a.parseCmd(ctx, in, lp, quick) return a.parseCmd(ctx, in, lp, mode)
}
func (a *Base) handleLabel(ctx context.Context, in inst.I) error {
if in.Label == "" {
return nil
}
addr := ctx.GetAddr()
// Munge for macros, relative labels, etc. If appropriate,
// set the last parent label.
parent := a.IsNewParentLabel(in.Label)
newL, err := a.FixLabel(ctx, in.Label)
if err != nil {
return in.Errorf("%v", err)
}
in.Label = newL
if parent {
ctx.SetLastLabel(in.Label)
}
lval, lok := ctx.Get(in.Label)
if lok && addr != lval {
return in.Errorf("Trying to set label '%s' to $%04x, but it already has value $%04x", in.Label, addr, lval)
}
ctx.Set(in.Label, addr)
return nil
} }
// 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(ctx context.Context, in inst.I, lp *lines.Parse, quick bool) (inst.I, error) { func (a *Base) parseCmd(ctx context.Context, in inst.I, lp *lines.Parse, mode flavors.ParseMode) (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)
} }
in.Command = lp.Emit() in.Command = lp.Emit()
if quick { if mode == flavors.ParseModeMacroSave {
// all we care about is labels (already covered) and end-of-macro. // all we care about is labels (already covered) and end-of-macro.
if dir, ok := a.Directives[in.Command]; ok { if dir, ok := a.Directives[in.Command]; ok {
if dir.Type == inst.TypeMacroEnd { if dir.Type == inst.TypeMacroEnd {
in.Type = inst.TypeMacroEnd in.Type = inst.TypeMacroEnd
} }
return in, nil
} }
return in, nil
} }
if mode == flavors.ParseModeInactive {
// all we care about are endif and else.
if dir, ok := a.Directives[in.Command]; ok {
if dir.Type == inst.TypeIfdefElse || dir.Type == inst.TypeIfdefEnd {
in.Type = dir.Type
// It's weird, but handle labels on else/endif lines.
if err := a.handleLabel(ctx, in); err != nil {
return in, err
}
}
}
return in, nil
}
// We don't need to handle labels if it's an equate.
if !a.EquateDirectives[in.Command] {
if err := a.handleLabel(ctx, in); err != nil {
return in, err
}
}
// Give ParseMacroCall a chance // Give ParseMacroCall a chance
if a.ParseMacroCall != nil { if a.ParseMacroCall != nil {
i, isMacro, err := a.ParseMacroCall(ctx, in, lp) i, isMacro, err := a.ParseMacroCall(ctx, in, lp)
@ -269,6 +309,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(ctx context.Context, 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]
@ -373,16 +414,21 @@ func (a *Base) parseOpArgs(ctx context.Context, in inst.I, lp *lines.Parse, summ
return common.DecodeOp(ctx, in, summary, indirect, xy, forceWide) return common.DecodeOp(ctx, in, summary, indirect, xy, forceWide)
} }
func (a *Base) ParseAddress(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseOrg(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
expr, err := a.parseExpression(ctx, in, lp) expr, err := a.parseExpression(ctx, in, lp)
if err != nil { if err != nil {
return in, err return in, err
} }
in.Exprs = append(in.Exprs, expr) in.Exprs = append(in.Exprs, expr)
val, err := expr.Eval(ctx, in.Line)
if err != nil {
return in, err
}
in.WidthKnown = true in.WidthKnown = true
in.Width = 0 in.Width = 0
in.Final = true in.Final = true
in.Addr = val
return in, nil return in, nil
} }

View File

@ -41,7 +41,7 @@ func newRedbook(name string) *RedBook {
r.DefaultOriginVal = 0x0800 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.ParseOrg, 0},
"OBJ": {inst.TypeNone, nil, 0}, "OBJ": {inst.TypeNone, nil, 0},
"ENDASM": {inst.TypeEnd, r.ParseNoArgDir, 0}, "ENDASM": {inst.TypeEnd, r.ParseNoArgDir, 0},
"EQU": {inst.TypeEqu, r.ParseEquate, inst.VarEquNormal}, "EQU": {inst.TypeEqu, r.ParseEquate, inst.VarEquNormal},
@ -59,6 +59,12 @@ func newRedbook(name string) *RedBook {
"REP": {inst.TypeNone, nil, 0}, // Repeat character "REP": {inst.TypeNone, nil, 0}, // Repeat character
"CHR": {inst.TypeNone, nil, 0}, // Set repeated character "CHR": {inst.TypeNone, nil, 0}, // Set repeated character
} }
r.EquateDirectives = map[string]bool{
"EQU": true,
"EPZ": true,
}
r.Operators = map[string]expr.Operator{ r.Operators = map[string]expr.Operator{
"*": expr.OpMul, "*": expr.OpMul,
"/": expr.OpDiv, "/": expr.OpDiv,

View File

@ -36,7 +36,7 @@ func New() *SCMA {
a.Directives = map[string]oldschool.DirectiveInfo{ a.Directives = map[string]oldschool.DirectiveInfo{
".IN": {inst.TypeInclude, a.ParseInclude, 0}, ".IN": {inst.TypeInclude, a.ParseInclude, 0},
".OR": {inst.TypeOrg, a.ParseAddress, 0}, ".OR": {inst.TypeOrg, a.ParseOrg, 0},
".TA": {inst.TypeTarget, a.ParseNotImplemented, 0}, ".TA": {inst.TypeTarget, a.ParseNotImplemented, 0},
".TF": {inst.TypeNone, nil, 0}, ".TF": {inst.TypeNone, nil, 0},
".EN": {inst.TypeEnd, a.ParseNoArgDir, 0}, ".EN": {inst.TypeEnd, a.ParseNoArgDir, 0},
@ -56,6 +56,11 @@ func New() *SCMA {
".EM": {inst.TypeMacroEnd, a.ParseNoArgDir, 0}, ".EM": {inst.TypeMacroEnd, a.ParseNoArgDir, 0},
".US": {inst.TypeNone, a.ParseNotImplemented, 0}, ".US": {inst.TypeNone, a.ParseNotImplemented, 0},
} }
a.EquateDirectives = map[string]bool{
".EQ": true,
}
a.Operators = map[string]expr.Operator{ a.Operators = map[string]expr.Operator{
"*": expr.OpMul, "*": expr.OpMul,
"/": expr.OpDiv, "/": expr.OpDiv,

View File

@ -10,6 +10,7 @@ import (
"github.com/zellyn/go6502/asm/flavors/merlin" "github.com/zellyn/go6502/asm/flavors/merlin"
"github.com/zellyn/go6502/asm/flavors/redbook" "github.com/zellyn/go6502/asm/flavors/redbook"
"github.com/zellyn/go6502/asm/flavors/scma" "github.com/zellyn/go6502/asm/flavors/scma"
"github.com/zellyn/go6502/asm/inst"
"github.com/zellyn/go6502/asm/lines" "github.com/zellyn/go6502/asm/lines"
) )
@ -233,26 +234,29 @@ func TestSimpleCommonFunctions(t *testing.T) {
ctx.AddMacroName("M1") ctx.AddMacroName("M1")
tt.f.InitContext(ctx) tt.f.InitContext(ctx)
inst, err := tt.f.ParseInstr(ctx, lines.NewSimple(tt.i), false) in, err := tt.f.ParseInstr(ctx, lines.NewSimple(tt.i), flavors.ParseModeNormal)
if err != nil { if err != nil {
t.Errorf(`%d. %s.ParseInstr("%s") => error: %s`, i, tt.f, 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 in.Line.Parse == nil {
t.Errorf("Got empty inst.Line.Parse on input '%s'", tt.i) t.Errorf("Got empty in.Line.Parse on input '%s'", tt.i)
} }
_, err = inst.Compute(ctx, true) if in.Type != inst.TypeOrg {
if err != nil { _, err = in.Compute(ctx, true)
t.Errorf(`%d. %s.ParseInstr("%s"): %s.Compute(tt.f, true) => error: %s`, i, tt.f, tt.i, inst, err)
continue if err != nil {
t.Errorf(`%d. %s.ParseInstr("%s"): %s.Compute(tt.f, true) => error: %s`, i, tt.f, tt.i, in, err)
continue
}
} }
if inst.String() != tt.p { if in.String() != tt.p {
t.Errorf(`%d. %s.ParseInstr("%s") = %s; want %s`, i, tt.f, tt.i, inst.String(), tt.p) t.Errorf(`%d. %s.ParseInstr("%s") = %s; want %s`, i, tt.f, tt.i, in.String(), tt.p)
continue continue
} }
if tt.b != "?" { if tt.b != "?" {
hx := hex.EncodeToString(inst.Data) hx := hex.EncodeToString(in.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.f, tt.i, hx, tt.b) t.Errorf(`%d. %s.ParseInstr("%s").Data = [%s]; want [%s]`, i, tt.f, tt.i, hx, tt.b)
@ -261,12 +265,12 @@ func TestSimpleCommonFunctions(t *testing.T) {
// Check length // Check length
w := uint16(len(tt.b) / 2) w := uint16(len(tt.b) / 2)
if !inst.WidthKnown { if !in.WidthKnown {
t.Errorf(`%d. %s.WidthKnown is false`, i, inst) t.Errorf(`%d. %s.WidthKnown is false`, i, in)
continue continue
} }
if inst.Width != w { if in.Width != w {
t.Errorf(`%d. %s.Width=%d; want %d`, i, inst, inst.Width, w) t.Errorf(`%d. %s.Width=%d; want %d`, i, in, in.Width, w)
continue continue
} }
} }
@ -297,9 +301,9 @@ func TestSimpleErrors(t *testing.T) {
continue continue
} }
ctx := &context.SimpleContext{} ctx := &context.SimpleContext{}
inst, err := tt.f.ParseInstr(ctx, lines.NewSimple(tt.i), false) in, err := tt.f.ParseInstr(ctx, lines.NewSimple(tt.i), flavors.ParseModeNormal)
if err == nil { if err == nil {
t.Errorf(`%d. %s.ParseInstr("%s") want err; got %s`, i, tt.f, tt.i, inst) t.Errorf(`%d. %s.ParseInstr("%s") want err; got %s`, i, tt.f, tt.i, in)
continue continue
} }
} }

View File

@ -192,11 +192,13 @@ func (i I) String() string {
// Compute attempts to finalize the instruction. // Compute attempts to finalize the instruction.
func (i *I) Compute(c context.Context, final bool) (bool, error) { func (i *I) Compute(c context.Context, final bool) (bool, error) {
if i.Type == TypeEqu || i.Type == TypeTarget || i.Type == TypeOrg { // xyzzy - remove
return i.computeMustKnow(c, final) if i.Type == TypeOrg {
panic("Compute called with TypeOrg")
return true, nil
} }
if err := i.computeLabel(c, final); err != nil { if i.Type == TypeEqu || i.Type == TypeTarget {
return false, err return i.computeMustKnow(c, final)
} }
if i.Final { if i.Final {
return true, nil return true, nil
@ -218,29 +220,6 @@ func (i *I) Compute(c context.Context, final bool) (bool, error) {
return true, nil return true, nil
} }
// computeLabel attempts to compute label values.
func (i *I) computeLabel(c context.Context, final bool) error {
if i.Label == "" {
return nil
}
if i.Type == TypeEqu {
panic("computeLabel should not be called for equates")
}
addr, aok := c.GetAddr()
if !aok {
return nil
}
lval, lok := c.Get(i.Label)
if lok && 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
}
func (i *I) computeData(c context.Context, final bool) (bool, error) { func (i *I) computeData(c context.Context, final bool) (bool, error) {
if len(i.Data) > 0 { if len(i.Data) > 0 {
i.WidthKnown = true i.WidthKnown = true
@ -335,9 +314,6 @@ func (i *I) computeMustKnow(c context.Context, final bool) (bool, error) {
// Don't handle labels. // Don't handle labels.
return true, nil return true, nil
} }
if err := i.computeLabel(c, final); err != nil {
return false, err
}
return true, nil return true, nil
} }
@ -401,14 +377,7 @@ func (i *I) computeOp(c context.Context, final bool) (bool, error) {
// It's a branch // It's a branch
if i.Mode == opcodes.MODE_RELATIVE { if i.Mode == opcodes.MODE_RELATIVE {
curr, ok := c.GetAddr() curr := c.GetAddr()
if !ok {
if final {
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) offset := int32(val) - (int32(curr) + 2)
if offset > 127 { if offset > 127 {
return false, i.Errorf("%s cannot jump forward %d (max 127) from $%04x to $%04x", i.Command, offset, curr+2, val) return false, i.Errorf("%s cannot jump forward %d (max 127) from $%04x to $%04x", i.Command, offset, curr+2, val)