Reorganize label fixing.

This commit is contained in:
Zellyn Hunter 2014-08-19 08:22:34 -07:00
parent 88ef7feee5
commit 54fe4809c1
16 changed files with 299 additions and 248 deletions

View File

@ -13,11 +13,10 @@ import (
)
type Assembler struct {
Flavor flavors.F
Opener lines.Opener
Insts []*inst.I
LastLabel string
Macros map[string]macros.M
Flavor flavors.F
Opener lines.Opener
Insts []*inst.I
Macros map[string]macros.M
}
func NewAssembler(flavor flavors.F, opener lines.Opener) *Assembler {
@ -44,14 +43,11 @@ func (a *Assembler) Load(filename string, prefix int) error {
if err != nil {
return err
}
// if line.Parse != nil {
// fmt.Fprintf("PLUGH: %s\n", line.Text())
// }
if done {
lineSources = lineSources[1:]
continue
}
in, parseErr := a.Flavor.ParseInstr(line)
in, parseErr := a.Flavor.ParseInstr(line, false)
if len(ifdefs) > 0 && !ifdefs[0] && in.Type != inst.TypeIfdefElse && in.Type != inst.TypeIfdefEnd {
// we're in an inactive ifdef branch
continue
@ -61,9 +57,9 @@ func (a *Assembler) Load(filename string, prefix int) error {
return err
}
if err := in.FixLabels(a.Flavor); err != nil {
return err
}
// if err := in.FixLabels(a.Flavor); err != nil {
// return err
// }
if _, err := a.passInst(&in, false); err != nil {
return err
@ -76,7 +72,6 @@ func (a *Assembler) Load(filename string, prefix int) error {
}
return line.Errorf("unknown instruction: %s", line.Parse.Text())
case inst.TypeMacroStart:
if err := a.readMacro(in, lineSources[0]); err != nil {
return err
}
@ -92,6 +87,12 @@ func (a *Assembler) Load(filename string, prefix int) error {
return in.Errorf(`error calling macro "%s": %v`, m.Name, err)
}
lineSources = append([]lines.LineSource{subLs}, lineSources...)
a.Flavor.PushMacroCall(m.Name, macroCall, m.Locals)
case inst.TypeMacroEnd:
// If we reached here, it's in a macro call, not a definition.
if !a.Flavor.PopMacroCall() {
return in.Errorf("unexpected end of macro")
}
case inst.TypeIfdef:
if len(in.Exprs) == 0 {
panic(fmt.Sprintf("Ifdef got parsed with no expression: %s", line))
@ -166,17 +167,16 @@ func (a *Assembler) readMacro(in inst.I, ls lines.LineSource) error {
if done {
return in.Errorf("end of file while reading macro %s", m.Name)
}
in2, err := a.Flavor.ParseInstr(line)
in2, err := a.Flavor.ParseInstr(line, true)
if a.Flavor.LocalMacroLabels() && in2.Label != "" {
m.Locals[in2.Label] = true
}
m.Lines = append(m.Lines, line.Parse.Text())
if err == nil && in2.Type == inst.TypeMacroEnd {
m.Lines = append(m.Lines, line.Parse.Text())
a.Macros[m.Name] = m
a.Flavor.AddMacroName(m.Name)
return nil
}
m.Lines = append(m.Lines, line.Parse.Text())
}
}
@ -246,7 +246,6 @@ func (a *Assembler) RawBytes() ([]byte, error) {
func (a *Assembler) Reset() {
a.Insts = nil
a.LastLabel = ""
}
func (a *Assembler) Membuf() (*membuf.Membuf, error) {

View File

@ -83,3 +83,45 @@ SOURCE FILE #01 =>FOO.TXT
* Sweet 16
http://amigan.1emu.net/kolsen/programming/sweet16.html
http://twimgs.com/informationweek/byte/archive/Apple-II-Description/The-Apple-II-by-Stephen-Wozniak.pdf
| R0 | Accumulator |
| R12 | Subroutine stack pointer |
| R13 | Comparison results |
| R14 | Status (prior result register << 8 + carry bit) |
| R15 | PC |
| 00 | Return to 6502 mode | RTN | 1 | |
| 01 | Branch always | BR | 2 | |
| 02 | Branch if no carry | BNC | 2 | |
| 03 | Branch if carry | BC | 2 | |
| 04 | Branch if plus | BP | 2 | |
| 05 | Branch if minus | BM | 2 | |
| 06 | Branch if zero | BZ | 2 | |
| 07 | Branch if nonzero | BNZ | 2 | |
| 08 | Branch if minus 1 | BM1 | 2 | |
| 09 | Branch if not minus 1 | BNM1 | 2 | |
| 0A | Break | BK | 1 | BRK |
| 0B | Return from subroutine | RS | 1 | PC <- [----R12] |
| 0C | Branch to subroutine | BS | 2 | [R12++++] <- PC(R15); PC(R15) += offset |
| 0D | - | | | |
| 0E | - | | | |
| 0F | - | | | |
| 1n | Set | SET R7 | 3 | Rn <- constant |
| 2n | Load | LD R7 | 1 | R0 <- Rn |
| 3n | Store | ST R7 | 1 | Rn <- R0 |
| 4n | Load indirect | LD @R7 | 1 | R0 <- byte[Rn++] |
| 5n | Store indirect | ST @R7 | 1 | byte[Rn++] <- R0 |
| 6n | Load double indirect | LDD @R7 | 1 | R0 <- word[Rn++++] |
| 7n | Store double indirect | STD @R3 | 1 | word[Rn++++] <- R0 |
| 8n | Pop indirect | POP @R3 | 1 | R0 <- byte[--Rn] |
| 9n | Store pop indirect | STP @R3 | 1 | byte[--Rn] <- R0 |
| An | Add | ADD R3 | 1 | R0 <- R0 + Rn |
| Bn | Sub | SUB R3 | 1 | R0 <- R0 - Rn |
| Cn | Pop double indirect | POPD @R3 | 1 | R0 <- word[----Rn] |
| Dn | Compare | CPR R3 | 1 | R13 <- A0 - Rn |
| En | Increment | INR R3 | 1 | Rn++ |
| Fn | Decrement | DCR R3 | 1 | Rn-- |
* Horrible error messages
** redbooka
CPY $#0 COMMENT TEXT

View File

@ -6,10 +6,8 @@ type Context interface {
Set(name string, value uint16)
Get(name string) (uint16, bool)
SetAddr(uint16)
ClearAddr(message string)
ClearMesg() string
GetAddr() (uint16, bool)
Zero() (uint16, error) // type ZeroFunc
DivZero() (uint16, error)
RemoveChanged()
Clear()
SettingOn(name string) error
@ -18,17 +16,29 @@ type Context interface {
HasSetting(name string) bool
AddMacroName(name string)
HasMacroName(name string) bool
PushMacroCall(name string, number int, locals map[string]bool)
PopMacroCall() bool
GetMacroCall() (string, int, map[string]bool)
LastLabel() string
SetLastLabel(label string)
}
type macroCall struct {
name string
number int
locals map[string]bool
}
type SimpleContext struct {
symbols map[string]symbolValue
addr int32
lastLabel string
clearMesg string // Saved message describing why Addr was cleared.
highbit byte // OR-mask for ASCII high bit
highbit byte // OR-mask for ASCII high bit
onOff map[string]bool
onOffDefaults map[string]bool
macroNames map[string]bool
macroCalls []macroCall
}
type symbolValue struct {
@ -42,8 +52,8 @@ func (sc *SimpleContext) fix() {
}
}
func (sc *SimpleContext) Zero() (uint16, error) {
return 0, fmt.Errorf("Not implemented: context.SimpleContext.Zero()")
func (sc *SimpleContext) DivZero() (uint16, error) {
return 0, fmt.Errorf("Not implemented: context.SimpleContext.DivZero()")
}
func (sc *SimpleContext) Get(name string) (uint16, bool) {
@ -55,19 +65,10 @@ func (sc *SimpleContext) Get(name string) (uint16, bool) {
return s.v, found
}
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
@ -156,3 +157,31 @@ func (sc *SimpleContext) SetOnOffDefaults(defaults map[string]bool) {
sc.onOffDefaults = defaults
sc.resetOnOff()
}
func (sc *SimpleContext) PushMacroCall(name string, number int, locals map[string]bool) {
sc.macroCalls = append(sc.macroCalls, macroCall{name, number, locals})
}
func (sc *SimpleContext) PopMacroCall() bool {
if len(sc.macroCalls) == 0 {
return false
}
sc.macroCalls = sc.macroCalls[0 : len(sc.macroCalls)-1]
return true
}
func (sc *SimpleContext) GetMacroCall() (string, int, map[string]bool) {
if len(sc.macroCalls) == 0 {
return "", 0, nil
}
mc := sc.macroCalls[len(sc.macroCalls)-1]
return mc.name, mc.number, mc.locals
}
func (sc *SimpleContext) LastLabel() string {
return sc.lastLabel
}
func (sc *SimpleContext) SetLastLabel(l string) {
sc.lastLabel = l
}

View File

@ -1,21 +0,0 @@
package context
/* Labeler is an interface that the flavors implement to handle label correction. */
type Labeler interface {
LastLabel() string
SetLastLabel(label string)
FixLabel(label string, macroCall int, locals map[string]bool) (string, error)
IsNewParentLabel(label string) bool
}
type LabelerBase struct {
lastLabel string
}
func (lb *LabelerBase) LastLabel() string {
return lb.lastLabel
}
func (lb *LabelerBase) SetLastLabel(l string) {
lb.lastLabel = l
}

View File

@ -174,7 +174,7 @@ func (e *E) Eval(ctx context.Context, ln *lines.Line) (uint16, error) {
return 0, nil
case OpDiv:
if r == 0 {
return ctx.Zero()
return ctx.DivZero()
}
return l / r, nil
case OpAnd:
@ -200,23 +200,3 @@ func (e *E) CheckedEval(ctx context.Context, ln *lines.Line) (val uint16, labelM
}
return val, false, err
}
// FixLabels attempts to turn .1 into LAST_LABEL.1, etc.
func (e *E) FixLabels(labeler context.Labeler, macroCall int, locals map[string]bool, ln *lines.Line) error {
newL, err := labeler.FixLabel(e.Text, macroCall, locals)
if err != nil {
return ln.Errorf("%v", err)
}
e.Text = newL
if e.Left != nil {
if err := e.Left.FixLabels(labeler, macroCall, locals, ln); err != nil {
return err
}
}
if e.Right != nil {
return e.Right.FixLabels(labeler, macroCall, locals, ln)
}
return nil
}

View File

@ -13,7 +13,6 @@ import (
type AS65 struct {
context.SimpleContext
context.LabelerBase
}
func New() *AS65 {
@ -21,7 +20,7 @@ func New() *AS65 {
}
// Parse an entire instruction, or return an appropriate error.
func (a *AS65) ParseInstr(line lines.Line) (inst.I, error) {
func (a *AS65) ParseInstr(line lines.Line, quick bool) (inst.I, error) {
return inst.I{}, nil
}
@ -41,10 +40,6 @@ func (a *AS65) IsNewParentLabel(label string) bool {
return label != "" && label[0] != '.'
}
func (a *AS65) FixLabel(label string, macroCall int, locals map[string]bool) (string, error) {
panic("AS65.FixLabel not implemented yet.")
}
func (a *AS65) LocalMacroLabels() bool {
return false
}

View File

@ -8,10 +8,14 @@ import (
"github.com/zellyn/go6502/opcodes"
)
const xyzzy = false
// DecodeOp contains the common code that decodes an Opcode, once we
// have fully parsed it.
func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect bool, xy rune, forceWide bool) (inst.I, error) {
ex := in.Exprs[0]
val, err := ex.Eval(c, in.Line)
valKnown := err == nil
// At this point we've parsed the Opcode. Let's see if it makes sense.
if indirect {
@ -69,6 +73,17 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
in.WidthKnown = true
in.Width = 2
in.Mode = opcodes.MODE_RELATIVE
in.Mode = inst.VarRelative
if valKnown && xyzzy {
b, err := RelativeAddr(c, in, val)
if err != nil {
return in, err
}
fmt.Printf("b=$%02x\n", b)
in.Data = []byte{in.Op, b}
in.Final = true
}
return in, nil
}
@ -82,6 +97,8 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
in.WidthKnown = true
in.Width = 2
in.Mode = opcodes.MODE_IMMEDIATE
in.Var = inst.VarBytes
//xyzzy()
return in, nil
}
@ -152,3 +169,20 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
in.Width = 2
return in, nil
}
func RelativeAddr(c context.Context, in inst.I, val uint16) (byte, error) {
curr, ok := c.GetAddr()
if !ok {
return 0, in.Errorf("cannot determine current address for '%s'", in.Command)
}
fmt.Printf("RelativeAddr: curr=%04x, val=%04x\n", curr, val)
// Found both current and target addresses
offset := int32(val) - (int32(curr) + 2)
if offset > 127 {
return 0, in.Errorf("%s cannot jump forward %d (max 127) from $%04x to $%04x", in.Command, offset, curr+2, val)
}
if offset < -128 {
return 0, in.Errorf("%s cannot jump back %d (max -128) from $%04x to $%04x", in.Command, offset, curr+2, val)
}
return byte(offset), nil
}

View File

@ -7,11 +7,10 @@ import (
)
type F interface {
ParseInstr(Line lines.Line) (inst.I, error)
ParseInstr(Line lines.Line, quick bool) (inst.I, error)
DefaultOrigin() uint16
ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error)
LocalMacroLabels() bool
String() string
context.Context
context.Labeler
}

View File

@ -43,14 +43,14 @@ func New() *Merlin {
"ORG": {inst.TypeOrg, m.ParseAddress, 0},
"OBJ": {inst.TypeNone, nil, 0},
"ENDASM": {inst.TypeEnd, m.ParseNoArgDir, 0},
"=": {inst.TypeEqu, m.ParseEquate, inst.EquNormal},
"HEX": {inst.TypeData, m.ParseHexString, inst.DataBytes},
"DFB": {inst.TypeData, m.ParseData, inst.DataBytes},
"DB": {inst.TypeData, m.ParseData, inst.DataBytes},
"DA": {inst.TypeData, m.ParseData, inst.DataWordsLe},
"DDB": {inst.TypeData, m.ParseData, inst.DataWordsBe},
"ASC": {inst.TypeData, m.ParseAscii, inst.DataAscii},
"DCI": {inst.TypeData, m.ParseAscii, inst.DataAsciiFlip},
"=": {inst.TypeEqu, m.ParseEquate, inst.VarEquNormal},
"HEX": {inst.TypeData, m.ParseHexString, inst.VarBytes},
"DFB": {inst.TypeData, m.ParseData, inst.VarBytes},
"DB": {inst.TypeData, m.ParseData, inst.VarBytes},
"DA": {inst.TypeData, m.ParseData, inst.VarWordsLe},
"DDB": {inst.TypeData, m.ParseData, inst.VarWordsBe},
"ASC": {inst.TypeData, m.ParseAscii, inst.VarAscii},
"DCI": {inst.TypeData, m.ParseAscii, inst.VarAsciiFlip},
".DO": {inst.TypeIfdef, m.ParseDo, 0},
".ELSE": {inst.TypeIfdefElse, m.ParseNoArgDir, 0},
".FIN": {inst.TypeIfdefEnd, m.ParseNoArgDir, 0},
@ -94,13 +94,13 @@ func New() *Merlin {
invertLast := in.Command == "DCI"
switch {
case !invert && !invertLast:
in.Var = inst.DataAscii
in.Var = inst.VarAscii
case !invert && invertLast:
in.Var = inst.DataAsciiFlip
in.Var = inst.VarAsciiFlip
case invert && !invertLast:
in.Var = inst.DataAsciiHi
in.Var = inst.VarAsciiHi
case invert && invertLast:
in.Var = inst.DataAsciiHiFlip
in.Var = inst.VarAsciiHiFlip
}
}
@ -153,6 +153,28 @@ func New() *Merlin {
return in, true, nil
}
m.FixLabel = func(label string) (string, error) {
_, macroCount, locals := m.GetMacroCall()
switch {
case label == "":
return label, nil
case label[0] == ':':
if last := m.LastLabel(); last == "" {
return "", fmt.Errorf("sublabel '%s' without previous label", label)
} else {
return fmt.Sprintf("%s/%s", last, label), nil
}
case locals[label]:
return fmt.Sprintf("%s{%d}", label, macroCount), nil
}
return label, nil
}
m.IsNewParentLabel = func(label string) bool {
return label != "" && label[0] != ':'
}
return m
}
@ -183,27 +205,6 @@ func (m *Merlin) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil
}
func (m *Merlin) IsNewParentLabel(label string) bool {
return label != "" && label[0] != ':'
}
func (m *Merlin) FixLabel(label string, macroCount int, locals map[string]bool) (string, error) {
switch {
case label == "":
return label, nil
case label[0] == ':':
if last := m.LastLabel(); last == "" {
return "", fmt.Errorf("sublabel '%s' without previous label", label)
} else {
return fmt.Sprintf("%s/%s", last, label), nil
}
case locals[label]:
return fmt.Sprintf("%s{%d}", label, macroCount), nil
}
return label, nil
}
func (m *Merlin) LocalMacroLabels() bool {
return true
}

View File

@ -44,7 +44,6 @@ type Base struct {
Directives map[string]DirectiveInfo
Operators map[string]expr.Operator
context.SimpleContext
context.LabelerBase
LabelChars string
LabelColons Requiredness
ExplicitARegister Requiredness
@ -57,13 +56,16 @@ type Base struct {
MsbChars string
LsbChars string
ImmediateChars string
ExtraCommenty func(string) bool
SetAsciiVariation func(*inst.I, *lines.Parse)
ParseMacroCall func(inst.I, *lines.Parse) (inst.I, bool, error)
operatorChars string
CharChars string
InvCharChars string
MacroArgSep string
LocalMacroLabels bool
ExtraCommenty func(string) bool
SetAsciiVariation func(*inst.I, *lines.Parse)
ParseMacroCall func(inst.I, *lines.Parse) (inst.I, bool, error)
FixLabel func(label string) (string, error)
IsNewParentLabel func(label string) bool
}
func (a *Base) String() string {
@ -71,7 +73,7 @@ func (a *Base) String() string {
}
// Parse an entire instruction, or return an appropriate error.
func (a *Base) ParseInstr(line lines.Line) (inst.I, error) {
func (a *Base) ParseInstr(line lines.Line, quick bool) (inst.I, error) {
lp := line.Parse
in := inst.I{Line: &line}
@ -119,6 +121,20 @@ func (a *Base) ParseInstr(line lines.Line) (inst.I, error) {
}
}
// 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(in.Label)
if err != nil {
return in, in.Errorf("%v", err)
}
in.Label = newL
if parent {
a.SetLastLabel(in.Label)
}
}
// Ignore whitespace at the start or after the label.
lp.IgnoreRun(Whitespace)
@ -126,22 +142,32 @@ func (a *Base) ParseInstr(line lines.Line) (inst.I, error) {
in.Type = inst.TypeNone
return in, nil
}
return a.ParseCmd(in, lp)
return a.parseCmd(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.
func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
func (a *Base) parseCmd(in inst.I, lp *lines.Parse, quick bool) (inst.I, error) {
if !lp.AcceptRun(cmdChars) && !(a.Directives["="].Func != nil && lp.Accept("=")) {
c := lp.Next()
return in, in.Errorf("expecting instruction, found '%c' (%d)", c, c)
}
in.Command = lp.Emit()
if quick {
// all we care about is labels (already covered) and end-of-macro.
if dir, ok := a.Directives[in.Command]; ok {
if dir.Type == inst.TypeMacroEnd {
in.Type = inst.TypeMacroEnd
}
return in, nil
}
}
// Give ParseMacroCall a chance
if a.ParseMacroCall != nil {
i, isMacro, err := a.ParseMacroCall(in, lp)
@ -163,12 +189,12 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
}
if a.HasSetting(in.Command) {
return a.ParseSetting(in, lp)
return a.parseSetting(in, lp)
}
if summary, ok := opcodes.ByName[in.Command]; ok {
in.Type = inst.TypeOp
return a.ParseOpArgs(in, lp, summary, false)
return a.parseOpArgs(in, lp, summary, false)
}
// Merlin lets you say "LDA:" or "LDA@" or "LDAZ" to force non-zero-page.
@ -177,14 +203,14 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
if summary, ok := opcodes.ByName[prefix]; ok {
in.Command = prefix
in.Type = inst.TypeOp
return a.ParseOpArgs(in, lp, summary, true)
return a.parseOpArgs(in, lp, summary, true)
}
}
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(in inst.I, lp *lines.Parse) (inst.I, error) {
in.Type = inst.TypeSetting
lp.IgnoreRun(Whitespace)
if !lp.AcceptRun(Letters) {
@ -208,17 +234,17 @@ func (a *Base) ParseSetting(in inst.I, lp *lines.Parse) (inst.I, error) {
// character of a macro argument.
func (a *Base) ParseMacroArg(in inst.I, lp *lines.Parse) (string, error) {
if lp.Peek() == '"' {
return a.ParseQuoted(in, lp)
return a.parseQuoted(in, lp)
}
lp.AcceptUntil(Whitespace + a.MacroArgSep)
return lp.Emit(), nil
}
// ParseQuoted parses a single quoted string macro argument. We expect
// parseQuoted parses a single quoted string macro argument. We expect
// to be looking at the first quote.
func (a *Base) ParseQuoted(in inst.I, lp *lines.Parse) (string, error) {
func (a *Base) parseQuoted(in inst.I, lp *lines.Parse) (string, error) {
if !lp.Consume(`"`) {
panic(fmt.Sprintf("ParseQuoted called not looking at a quote"))
panic(fmt.Sprintf("parseQuoted called not looking at a quote"))
}
for {
lp.AcceptUntil(`"`)
@ -241,9 +267,9 @@ func (a *Base) ParseQuoted(in inst.I, lp *lines.Parse) (string, error) {
return strings.Replace(s, `""`, `"`, -1), nil
}
// 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)
func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary, forceWide bool) (inst.I, error) {
func (a *Base) parseOpArgs(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.
if summary.Modes == opcodes.MODE_IMPLIED {
op := summary.Ops[0]
@ -366,12 +392,12 @@ func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
a.SetAsciiVariation(&in, lp)
var invert, invertLast byte
switch in.Var {
case inst.DataAscii:
case inst.DataAsciiHi:
case inst.VarAscii:
case inst.VarAsciiHi:
invert = 0x80
case inst.DataAsciiFlip:
case inst.VarAsciiFlip:
invertLast = 0x80
case inst.DataAsciiHiFlip:
case inst.VarAsciiHiFlip:
invert = 0x80
invertLast = 0x80
default:
@ -662,6 +688,11 @@ func (a *Base) ParseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) {
ex.Op = expr.OpLeaf
ex.Text = lp.Emit()
newL, err := a.FixLabel(ex.Text)
if err != nil {
return &expr.E{}, in.Errorf("%v", err)
}
ex.Text = newL
return top, nil
}
@ -680,11 +711,11 @@ func (a *Base) ReplaceMacroArgs(line string, args []string, kwargs map[string]st
return line, err
}
func (a *Base) IsNewParentLabel(label string) bool {
func (a *Base) DefaultIsNewParentLabel(label string) bool {
return label != "" && label[0] != '.'
}
func (a *Base) FixLabel(label string, macroCall int, locals map[string]bool) (string, error) {
func (a *Base) DefaultFixLabel(label string) (string, error) {
switch {
case label == "":
return label, nil
@ -695,6 +726,7 @@ func (a *Base) FixLabel(label string, macroCall int, locals map[string]bool) (st
return fmt.Sprintf("%s/%s", last, label), nil
}
case label[0] == ':':
_, macroCall, _ := a.GetMacroCall()
if macroCall == 0 {
return "", fmt.Errorf("macro-local label '%s' seen outside macro", label)
} else {

View File

@ -42,14 +42,14 @@ func newRedbook(name string) *RedBook {
"ORG": {inst.TypeOrg, r.ParseAddress, 0},
"OBJ": {inst.TypeNone, nil, 0},
"ENDASM": {inst.TypeEnd, r.ParseNoArgDir, 0},
"EQU": {inst.TypeEqu, r.ParseEquate, inst.EquNormal},
"EPZ": {inst.TypeEqu, r.ParseEquate, inst.EquPageZero},
"DFB": {inst.TypeData, r.ParseData, inst.DataBytes},
"DW": {inst.TypeData, r.ParseData, inst.DataWordsLe},
"DDB": {inst.TypeData, r.ParseData, inst.DataWordsBe},
"ASC": {inst.TypeData, r.ParseAscii, inst.DataAscii},
"DCI": {inst.TypeData, r.ParseAscii, inst.DataAsciiFlip},
"HEX": {inst.TypeData, r.ParseHexString, inst.DataBytes},
"EQU": {inst.TypeEqu, r.ParseEquate, inst.VarEquNormal},
"EPZ": {inst.TypeEqu, r.ParseEquate, inst.VarEquPageZero},
"DFB": {inst.TypeData, r.ParseData, inst.VarBytes},
"DW": {inst.TypeData, r.ParseData, inst.VarWordsLe},
"DDB": {inst.TypeData, r.ParseData, inst.VarWordsBe},
"ASC": {inst.TypeData, r.ParseAscii, inst.VarAscii},
"DCI": {inst.TypeData, r.ParseAscii, inst.VarAsciiFlip},
"HEX": {inst.TypeData, r.ParseHexString, inst.VarBytes},
"PAGE": {inst.TypeNone, nil, 0}, // New page
"TITLE": {inst.TypeNone, nil, 0}, // Title
"SBTL": {inst.TypeNone, nil, 0}, // Subtitle
@ -75,19 +75,22 @@ func newRedbook(name string) *RedBook {
r.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) {
if in.Command == "ASC" {
if r.Setting("MSB") {
in.Var = inst.DataAsciiHi
in.Var = inst.VarAsciiHi
} else {
in.Var = inst.DataAscii
in.Var = inst.VarAscii
}
return
}
if in.Command == "DCI" {
in.Var = inst.DataAsciiFlip
in.Var = inst.VarAsciiFlip
} else {
panic(fmt.Sprintf("Unknown ascii directive: '%s'", in.Command))
}
}
r.FixLabel = r.DefaultFixLabel
r.IsNewParentLabel = r.DefaultIsNewParentLabel
return r
}

View File

@ -37,10 +37,10 @@ func New() *SCMA {
".TF": {inst.TypeNone, nil, 0},
".EN": {inst.TypeEnd, a.ParseNoArgDir, 0},
".EQ": {inst.TypeEqu, a.ParseEquate, 0},
".DA": {inst.TypeData, a.ParseData, inst.DataMixed},
".HS": {inst.TypeData, a.ParseHexString, inst.DataBytes},
".AS": {inst.TypeData, a.ParseAscii, inst.DataBytes},
".AT": {inst.TypeData, a.ParseAscii, inst.DataBytes},
".DA": {inst.TypeData, a.ParseData, inst.VarMixed},
".HS": {inst.TypeData, a.ParseHexString, inst.VarBytes},
".AS": {inst.TypeData, a.ParseAscii, inst.VarBytes},
".AT": {inst.TypeData, a.ParseAscii, inst.VarBytes},
".BS": {inst.TypeBlock, a.ParseBlockStorage, 0},
".TI": {inst.TypeNone, nil, 0},
".LIST": {inst.TypeNone, nil, 0},
@ -73,13 +73,13 @@ func New() *SCMA {
invertLast := in.Command == ".AT"
switch {
case !invert && !invertLast:
in.Var = inst.DataAscii
in.Var = inst.VarAscii
case !invert && invertLast:
in.Var = inst.DataAsciiFlip
in.Var = inst.VarAsciiFlip
case invert && !invertLast:
in.Var = inst.DataAsciiHi
in.Var = inst.VarAsciiHi
case invert && invertLast:
in.Var = inst.DataAsciiHiFlip
in.Var = inst.VarAsciiHiFlip
}
}
@ -112,6 +112,9 @@ func New() *SCMA {
return in, true, nil
}
a.FixLabel = a.DefaultFixLabel
a.IsNewParentLabel = a.DefaultIsNewParentLabel
return a
}

View File

@ -2,6 +2,7 @@ package flavors
import (
"encoding/hex"
"fmt"
"reflect"
"strings"
"testing"
@ -292,6 +293,7 @@ func TestMultiline(t *testing.T) {
if !tt.active {
continue
}
fmt.Println(tt.name)
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)
}
@ -302,41 +304,34 @@ func TestMultiline(t *testing.T) {
o[k] = strings.Join(v, "\n")
}
if err := tt.a.Load("TESTFILE", 0); err != nil {
t.Errorf(`%d("%s" - %s): tt.a.Load("TESTFILE") failed: %s`, i, tt.name, tt.a.Flavor, err)
continue
t.Fatalf(`%d("%s" - %s): tt.a.Load("TESTFILE") failed: %s`, i, tt.name, tt.a.Flavor, err)
}
isFinal, err := tt.a.Pass(true)
if err != nil {
t.Errorf(`%d("%s" - %s): tt.a.Pass(true) failed: %s`, i, tt.name, tt.a.Flavor, err)
continue
t.Fatalf(`%d("%s" - %s): tt.a.Pass(true) failed: %s`, i, tt.name, tt.a.Flavor, err)
}
if !isFinal {
t.Errorf(`%d("%s" - %s): tt.a.Pass(true) couldn't finalize`, i, tt.name, tt.a.Flavor)
continue
t.Fatalf(`%d("%s" - %s): tt.a.Pass(true) couldn't finalize`, i, tt.name, tt.a.Flavor)
}
if tt.b != "" {
bb, err := tt.a.RawBytes()
if err != nil {
t.Errorf(`%d("%s" - %s): tt.a.RawBytes() failed: %s`, i, tt.name, tt.a.Flavor, err)
continue
t.Fatalf(`%d("%s" - %s): tt.a.RawBytes() failed: %s`, i, tt.name, tt.a.Flavor, err)
}
hx := hex.EncodeToString(bb)
if hx != tt.b {
t.Errorf(`%d("%s" - %s): tt.a.RawBytes()=[%s]; want [%s]`, i, tt.name, tt.a.Flavor, hx, tt.b)
continue
t.Fatalf(`%d("%s" - %s): tt.a.RawBytes()=[%s]; want [%s]`, i, tt.name, tt.a.Flavor, hx, tt.b)
}
}
if len(tt.ps) != 0 {
m, err := tt.a.Membuf()
if err != nil {
t.Errorf(`%d("%s" - %s): tt.a.Membuf() failed: %s`, i, tt.name, tt.a.Flavor, err)
continue
t.Fatalf(`%d("%s" - %s): tt.a.Membuf() failed: %s`, i, tt.name, tt.a.Flavor, err)
}
ps := m.Pieces()
if !reflect.DeepEqual(ps, tt.ps) {
t.Errorf(`%d("%s" - %s): tt.Membuf().Pieces() = %v; want %v`, i, tt.name, tt.a.Flavor, ps, tt.ps)
continue
t.Fatalf(`%d("%s" - %s): tt.Membuf().Pieces() = %v; want %v`, i, tt.name, tt.a.Flavor, ps, tt.ps)
}
}
}

View File

@ -229,7 +229,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
tt.a.AddMacroName("INCW")
tt.a.AddMacroName("M1")
inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i))
inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i), false)
if err != nil {
t.Errorf(`%d. %s.ParseInstr("%s") => error: %s`, i, tt.a, tt.i, err)
continue
@ -293,7 +293,7 @@ func TestSimpleErrors(t *testing.T) {
if tt.a != ss {
continue
}
inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i))
inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i), false)
if err == nil {
t.Errorf(`%d. %s.ParseInstr("%s") want err; got %s`, i, tt.a, tt.i, inst)
continue

View File

@ -36,22 +36,20 @@ const (
TypeSetting // An on/off setting toggle
)
// Variants for "TypeData" instructions.
// Variants for instructions. These tell the instruction how to
// interpret the raw data that comes in on the first or second pass.
const (
DataBytes = iota // Data: expressions, but forced to one byte per
DataMixed // Bytes or words (LE), depending on individual expression widths
DataWordsLe // Data: expressions, but forced to one word per, little-endian
DataWordsBe // Data: expressions, but forced to one word per, big-endian
DataAscii // Data: from ASCII strings, high bit clear
DataAsciiFlip // Data: from ASCII strings, high bit clear, except last char
DataAsciiHi // Data: from ASCII strings, high bit set
DataAsciiHiFlip // Data: from ASCII strings, high bit set, except last char
)
// Variants for "TypeEqu" instructions.
const (
EquNormal = iota
EquPageZero
VarBytes = iota // Data: expressions, but forced to one byte per
VarMixed // Bytes or words (LE), depending on individual expression widths
VarWordsLe // Data: expressions, but forced to one word per, little-endian
VarWordsBe // Data: expressions, but forced to one word per, big-endian
VarAscii // Data: from ASCII strings, high bit clear
VarAsciiFlip // Data: from ASCII strings, high bit clear, except last char
VarAsciiHi // Data: from ASCII strings, high bit set
VarAsciiHiFlip // Data: from ASCII strings, high bit set, except last char
VarRelative // For branches: a one-byte relative address
VarEquNormal // Equ: a normal equate
VarEquPageZero // Equ: a page-zero equate
)
type I struct {
@ -98,15 +96,15 @@ func (i I) TypeString() string {
return "inc"
case TypeData:
switch i.Var {
case DataMixed:
case VarMixed:
return "data"
case DataBytes:
case VarBytes:
return "data/b"
case DataWordsLe:
case VarWordsLe:
return "data/wle"
case DataWordsBe:
case VarWordsBe:
return "data/wbe"
case DataAscii, DataAsciiHi, DataAsciiFlip, DataAsciiHiFlip:
case VarAscii, VarAsciiHi, VarAsciiFlip, VarAsciiHiFlip:
return "data/b"
default:
panic(fmt.Sprintf("unknown data variant: %d", i.Var))
@ -217,29 +215,6 @@ func (i *I) Compute(c context.Context, final bool) (bool, error) {
return true, nil
}
// FixLabels attempts to turn .1 into LAST_LABEL.1, etc.
func (i *I) FixLabels(labeler context.Labeler) error {
macroCall := i.Line.GetMacroCall()
macroLocals := i.Line.GetMacroLocals()
parent := labeler.IsNewParentLabel(i.Label)
newL, err := labeler.FixLabel(i.Label, macroCall, macroLocals)
if err != nil {
return i.Errorf("%v", err)
}
i.Label = newL
if parent {
labeler.SetLastLabel(i.Label)
}
for _, e := range i.Exprs {
if err := e.FixLabels(labeler, macroCall, macroLocals, i.Line); err != nil {
return err
}
}
return nil
}
// computeLabel attempts to compute equates and label values.
func (i *I) computeLabel(c context.Context, final bool) error {
if i.Label == "" {
@ -277,11 +252,11 @@ func (i *I) computeData(c context.Context, final bool) (bool, error) {
for _, e := range i.Exprs {
var w uint16
switch i.Var {
case DataMixed:
case VarMixed:
w = e.Width()
case DataBytes:
case VarBytes:
w = 1
case DataWordsLe, DataWordsBe:
case VarWordsLe, VarWordsBe:
w = 2
}
width += w
@ -296,18 +271,18 @@ func (i *I) computeData(c context.Context, final bool) (bool, error) {
}
}
switch i.Var {
case DataMixed:
case VarMixed:
switch w {
case 1:
data = append(data, byte(val))
case 2:
data = append(data, byte(val), byte(val>>8))
}
case DataBytes:
case VarBytes:
data = append(data, byte(val))
case DataWordsLe:
case VarWordsLe:
data = append(data, byte(val), byte(val>>8))
case DataWordsBe:
case VarWordsBe:
data = append(data, byte(val>>8), byte(val))
default:
panic(fmt.Sprintf("Unknown data variant handed to computeData: %d", i.Var))

View File

@ -13,7 +13,7 @@ func TestComputeLabel(t *testing.T) {
Label: "L1",
}
c := &context.SimpleContext{}
i.computeLabel(c, false, false)
i.computeLabel(c, false)
}
func TestWidthDoesNotChange(t *testing.T) {
@ -27,8 +27,7 @@ func TestWidthDoesNotChange(t *testing.T) {
Right: &expr.E{Op: expr.OpLeaf, Text: "L2"},
},
},
MinWidth: 0x2,
MaxWidth: 0x3,
Width: 0x2,
Final: false,
Op: 0xad,
Mode: 0x2,
@ -38,15 +37,7 @@ func TestWidthDoesNotChange(t *testing.T) {
}
c := &context.SimpleContext{}
c.Set("L1", 0x102)
final, err := i.Compute(c, false, false)
if err != nil {
t.Fatal(err)
}
if final {
t.Fatal("First pass shouldn't be able to finalize expression with unknown width")
}
final, err = i.Compute(c, true, false)
final, err := i.Compute(c, false)
if err != nil {
t.Fatal(err)
}
@ -56,16 +47,13 @@ func TestWidthDoesNotChange(t *testing.T) {
if !i.WidthKnown {
t.Fatal("Second pass should have set width.")
}
if i.MinWidth != i.MaxWidth {
t.Fatalf("i.WidthKnown, but i.MinWidth(%d) != i.MaxWidth(%d)", i.MinWidth, i.MaxWidth)
}
if i.MinWidth != 3 {
t.Fatalf("i.MinWidth should be 3; got %d", i.MinWidth)
if i.Width != 3 {
t.Fatalf("i.Width should be 3; got %d", i.Width)
}
c.Set("L2", 0x101)
final, err = i.Compute(c, true, true)
final, err = i.Compute(c, true)
if err != nil {
t.Fatal(err)
}
@ -75,10 +63,7 @@ func TestWidthDoesNotChange(t *testing.T) {
if !i.WidthKnown {
t.Fatal("Third pass should left width unchanged.")
}
if i.MinWidth != i.MaxWidth {
t.Fatalf("i.WidthKnown, but i.MinWidth(%d) != i.MaxWidth(%d)", i.MinWidth, i.MaxWidth)
}
if i.MinWidth != 3 {
t.Fatalf("i.MinWidth should still be 3; got %d", i.MinWidth)
if i.Width != 3 {
t.Fatalf("i.Width should still be 3; got %d", i.Width)
}
}