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:
parent
cab917364e
commit
c07a3732a6
38
asm/asm.go
38
asm/asm.go
@ -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)
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user