monitor.asm assembles now

This commit is contained in:
Zellyn Hunter 2014-06-03 08:46:49 -07:00
parent 0a28127420
commit 255fa86640
12 changed files with 199 additions and 20 deletions

View File

@ -36,6 +36,7 @@ var outfile = flag.String("out", "", "output file")
var listfile = flag.String("listing", "", "listing file")
var format = flag.String("format", "binary", "output format: binary/ihex")
var fill = flag.Uint("fillbyte", 0x00, "byte value to use when filling gaps between assmebler output regions")
var prefix = flag.Int("prefix", -1, "length of prefix to skip past addresses and bytes, -1 to guess")
func main() {
flag.Parse()
@ -69,7 +70,17 @@ func main() {
var o lines.OsOpener
a := asm.NewAssembler(f, o)
if err := a.Assemble(*infile); err != nil {
p := *prefix
if p < 0 {
var err error
p, err = lines.GuessFilePrefixSize(*infile, o)
if err != nil {
fmt.Fprintf(os.Stderr, "Error trying to determine prefix length for file '%s'", *infile, err)
os.Exit(1)
}
}
if err := a.AssembleWithPrefix(*infile, p); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

View File

@ -29,10 +29,10 @@ func NewAssembler(flavor flavors.F, opener lines.Opener) *Assembler {
}
// Load loads a new assembler file, deleting any previous data.
func (a *Assembler) Load(filename string) error {
func (a *Assembler) Load(filename string, prefix int) error {
a.initPass()
context := lines.Context{Filename: filename}
ls, err := lines.NewFileLineSource(filename, context, a.Opener)
ls, err := lines.NewFileLineSource(filename, context, a.Opener, prefix)
if err != nil {
return err
}
@ -48,7 +48,7 @@ func (a *Assembler) Load(filename string) error {
lineSources = lineSources[1:]
continue
}
in, err := a.Flavor.ParseInstr(line)
in, parseErr := a.Flavor.ParseInstr(line)
if len(ifdefs) > 0 && !ifdefs[0] && in.Type != inst.TypeIfdefElse && in.Type != inst.TypeIfdefEnd {
// we're in an inactive ifdef branch
continue
@ -63,6 +63,9 @@ func (a *Assembler) Load(filename string) error {
switch in.Type {
case inst.TypeUnknown:
if parseErr != nil {
return parseErr
}
return line.Errorf("unknown instruction: %s", line.Parse.Text())
case inst.TypeMacroStart:
if err := a.readMacro(in, lineSources[0]); err != nil {
@ -104,7 +107,7 @@ func (a *Assembler) Load(filename string) error {
case inst.TypeInclude:
filename = filepath.Join(filepath.Dir(in.Line.Context.Filename), in.TextArg)
subContext := lines.Context{Filename: filename, Parent: in.Line}
subLs, err := lines.NewFileLineSource(filename, subContext, a.Opener)
subLs, err := lines.NewFileLineSource(filename, subContext, a.Opener, prefix)
if err != nil {
return in.Errorf("error including file: %v", err)
}
@ -123,8 +126,12 @@ func (a *Assembler) Load(filename string) error {
}
func (a *Assembler) Assemble(filename string) error {
return a.AssembleWithPrefix(filename, 0)
}
func (a *Assembler) AssembleWithPrefix(filename string, prefix int) error {
a.Reset()
if err := a.Load(filename); err != nil {
if err := a.Load(filename, prefix); err != nil {
return err
}

View File

@ -68,3 +68,14 @@ http://mirrors.apple2.org.za/ftp.apple.asimov.net/images/programming/assembler/E
** How to run
Boot from the disk
type "- edasm.system"
** Differences from manual
The manual claims that "MSB ON" is the default.
:ASM FOO.TXT
SOURCE FILE #01 =>FOO.TXT
0000:41 42 43 1 ASC "ABC"
0003: 2 MSB OFF
0003:41 42 43 3 ASC "ABC"
0006: 4 MSB ON
0006:C1 C2 C3 5 ASC "ABC"

View File

@ -13,6 +13,10 @@ type Context interface {
RemoveChanged()
AddrKnown() bool
Clear()
SettingOn(name string) error
SettingOff(name string) error
Setting(name string) bool
HasSetting(name string) bool
}
type SimpleContext struct {
@ -21,6 +25,7 @@ type SimpleContext struct {
lastLabel string
clearMesg string // Saved message describing why Addr was cleared.
highbit byte // OR-mask for ASCII high bit
OnOff map[string]bool
}
type symbolValue struct {
@ -94,3 +99,36 @@ func (sc *SimpleContext) Clear() {
sc.symbols = make(map[string]symbolValue)
sc.highbit = 0x00
}
func (sc *SimpleContext) SettingOn(name string) error {
if !sc.HasSetting(name) {
return fmt.Errorf("no settable variable named '%s'", name)
}
if sc.OnOff == nil {
sc.OnOff = map[string]bool{name: true}
} else {
sc.OnOff[name] = true
}
return nil
}
func (sc *SimpleContext) SettingOff(name string) error {
if !sc.HasSetting(name) {
return fmt.Errorf("no settable variable named '%s'", name)
}
if sc.OnOff == nil {
sc.OnOff = map[string]bool{name: false}
} else {
sc.OnOff[name] = false
}
return nil
}
func (sc *SimpleContext) Setting(name string) bool {
return sc.OnOff[name]
}
func (sc *SimpleContext) HasSetting(name string) bool {
_, ok := sc.OnOff[name]
return ok
}

View File

@ -83,7 +83,7 @@ func (a *Base) ParseInstr(line lines.Line) (inst.I, error) {
// Empty line or comment
trimmed := strings.TrimSpace(lp.Rest())
if trimmed == "" || trimmed[0] == '*' {
if trimmed == "" || trimmed[0] == '*' || trimmed[0] == ';' {
in.Type = inst.TypeNone
return in, nil
}
@ -141,6 +141,10 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
return dir.Func(in, lp)
}
if a.HasSetting(in.Command) {
return a.ParseSetting(in, lp)
}
if summary, ok := opcodes.ByName[in.Command]; ok {
in.Type = inst.TypeOp
return a.ParseOpArgs(in, lp, summary)
@ -148,6 +152,26 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
return inst.I{}, in.Errorf(`unknown command/instruction: "%s"`, in.Command)
}
func (a *Base) ParseSetting(in inst.I, lp *lines.Parse) (inst.I, error) {
in.Type = inst.TypeSetting
lp.IgnoreRun(whitespace)
if !lp.AcceptRun(Letters) {
c := lp.Next()
return inst.I{}, in.Errorf("expecting ON/OFF, found '%s'", c)
}
in.TextArg = lp.Emit()
switch in.TextArg {
case "ON":
a.SettingOn(in.Command)
case "OFF":
a.SettingOff(in.Command)
default:
return inst.I{}, in.Errorf("expecting ON/OFF, found '%s'", in.TextArg)
}
return in, nil
}
// ParseMacroCall parses a macro call. We expect to be looking at a the
// first character of the macro name.
func (a *Base) ParseMacroCall(in inst.I, lp *lines.Parse) (inst.I, error) {

View File

@ -12,7 +12,6 @@ import (
// RedBook implements a Redbook-listing-compatible-ish assembler flavor.
type RedBook struct {
oldschool.Base
asciiHi bool
}
func New() *RedBook {
@ -45,7 +44,6 @@ func New() *RedBook {
".EM": {inst.TypeMacroEnd, a.ParseNoArgDir, 0},
".US": {inst.TypeNone, a.ParseNotImplemented, 0},
"PAGE": {inst.TypeNone, nil, 0}, // New page
"LST": {inst.TypeNone, nil, 0}, // Listing on/off
"SBTL": {inst.TypeNone, nil, 0}, // Subtitle
"SKP": {inst.TypeNone, nil, 0}, // Skip lines
"REP": {inst.TypeNone, nil, 0}, // Repeat character
@ -61,9 +59,14 @@ func New() *RedBook {
"=": expr.OpEq,
}
a.OnOff = map[string]bool{
"MSB": true, // MSB defaults to true, as per manual
"LST": true, // Display listing: not used
}
a.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) {
if in.Command == "ASC" {
if a.asciiHi {
if a.Setting("MSB") {
in.Var = inst.DataAsciiHi
} else {
in.Var = inst.DataAscii

View File

@ -7,6 +7,7 @@ import (
"testing"
"github.com/zellyn/go6502/asm"
"github.com/zellyn/go6502/asm/flavors/redbook"
"github.com/zellyn/go6502/asm/flavors/scma"
"github.com/zellyn/go6502/asm/lines"
"github.com/zellyn/go6502/asm/membuf"
@ -25,6 +26,7 @@ func TestMultiline(t *testing.T) {
o := lines.NewTestOpener()
ss := asm.NewAssembler(scma.New(), o)
rb := asm.NewAssembler(redbook.New(), o)
// aa := asm.NewAssembler(as65.New(), o)
// mm := asm.NewAssembler(merlin.New(), o)
@ -216,6 +218,15 @@ func TestMultiline(t *testing.T) {
{0x1000, h("a901a903ea")},
{0x2000, h("a902")},
}, true},
// Check turning MSB on and off
{rb, "MSB toggle", []string{
" ASC 'AB'",
" MSB OFF",
" ASC 'AB'",
" MSB ON",
" ASC 'AB'",
}, nil, "c1c24142c1c2", nil, true},
}
for i, tt := range tests {
@ -231,7 +242,7 @@ func TestMultiline(t *testing.T) {
for k, v := range tt.ii {
o[k] = strings.Join(v, "\n")
}
if err := tt.a.Load("TESTFILE"); err != nil {
if err := tt.a.Load("TESTFILE", 0); err != nil {
t.Errorf(`%d("%s"): tt.a.Load("TESTFILE") failed: %s`, i, tt.name, err)
continue
}

View File

@ -26,6 +26,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
}{
{ss, "* Comment", "{-}", ""},
{rb, "* Comment", "{-}", ""},
{rb, " ; Comment", "{-}", ""},
{aa, "; Comment", "{-}", ""},
{mm, "* Comment", "{-}", ""},
{ss, " far-out-comment", "{-}", ""},
@ -121,11 +122,11 @@ func TestSimpleCommonFunctions(t *testing.T) {
{ss, ` .AT -"ABC"`, "{data/b}", "c1c243"},
{ss, ` .AS -dABCd`, "{data/b}", "c1c2c3"},
{ss, ` .AT -dABCd`, "{data/b}", "c1c243"},
{rb, ` ASC "ABC"`, "{data/b}", "414243"},
{rb, ` ASC $ABC$ ;comment`, "{data/b}", "414243"},
{rb, ` ASC $ABC`, "{data/b}", "414243"},
{rb, ` ASC "ABC"`, "{data/b}", "c1c2c3"},
{rb, ` ASC $ABC$ ;comment`, "{data/b}", "c1c2c3"},
{rb, ` ASC $ABC`, "{data/b}", "c1c2c3"},
{rb, ` DCI "ABC"`, "{data/b}", "4142c3"},
{rb, ` ASC -ABC-`, "{data/b}", "414243"},
{rb, ` ASC -ABC-`, "{data/b}", "c1c2c3"},
{ss, " .HS 0001ffAb", "{data/b}", "0001ffab"},
{ss, "A.B .EQ *-C.D", "{= 'A.B' (- * C.D)}", ""},
{ss, " .BS $8", "{block $0008}", "xxxxxxxxxxxxxxxx"},
@ -140,6 +141,10 @@ func TestSimpleCommonFunctions(t *testing.T) {
{ss, " LDX #']+$80", "{LDX/imm (lsb (+ $005d $0080))}", "a2dd"},
{ss, " CMP #';'+1", "{CMP/imm (lsb (+ $003b $0001))}", "c93c"},
{rb, " LST ON", "{set LST ON}", ""},
{rb, " LST OFF", "{set LST OFF}", ""},
{rb, " MSB ON", "{set MSB ON}", ""},
{rb, " MSB OFF", "{set MSB OFF}", ""},
}
// TODO(zellyn): Add tests for finalization of four SCMA directives:

View File

@ -33,6 +33,7 @@ const (
TypeEqu // Equate
TypeOp // An actual asm opcode
TypeEnd // End assembly
TypeSetting // An on/off setting toggle
)
// Variants for "TypeData" instructions.
@ -118,6 +119,8 @@ func (i I) TypeString() string {
return "="
case TypeEnd:
return "end"
case TypeSetting:
return "set"
case TypeOp:
modeStr := "?"
switch i.Mode {
@ -158,6 +161,8 @@ func (i I) String() string {
switch i.Type {
case TypeInclude:
return fmt.Sprintf("{inc '%s'}", i.TextArg)
case TypeSetting:
return fmt.Sprintf("{set %s %s}", i.Command, i.TextArg)
}
s := "{" + i.TypeString()
if i.Label != "" {

View File

@ -19,7 +19,10 @@ func (o OsOpener) Open(filename string) (io.ReadCloser, error) {
return os.Open(filename)
}
func NewFileLineSource(filename string, context Context, opener Opener) (LineSource, error) {
func NewFileLineSource(filename string, context Context, opener Opener, prefix int) (LineSource, error) {
if prefix < 0 {
return nil, fmt.Errorf("NewFileLineSource: want prefix >= 0; got %d", prefix)
}
var ls []string
file, err := opener.Open(filename)
if err != nil {
@ -35,7 +38,7 @@ func NewFileLineSource(filename string, context Context, opener Opener) (LineSou
return nil, err
}
return NewSimpleLineSource(context, ls), nil
return NewSimpleLineSource(context, ls, prefix), nil
}
type TestOpener map[string]string
@ -58,3 +61,57 @@ func (o TestOpener) Clear() {
func NewTestOpener() TestOpener {
return make(TestOpener)
}
var whitespace = " \t\f\r\n"
var prefixChars = "abcdefABCDEF0123456789:"
func linePrefix(line string) int {
start := 0
for i, c := range line {
switch {
case strings.IndexRune(whitespace, c) >= 0:
start = i + 1
case strings.IndexRune(prefixChars, c) < 0:
return start
}
}
return -1
}
// GuessFilePrefixSize takes an assembly listing with address, bytes,
// and possibly line numbers, and tries to guess the size of the
// prefix that will skip past all that, to the label column.
func GuessFilePrefixSize(filename string, opener Opener) (prefix int, err error) {
counts := make(map[int]int)
max := 0
lines := 0
rc, err := opener.Open(filename)
if err != nil {
return 0, err
}
defer func() {
err = rc.Close()
}()
scanner := bufio.NewScanner(rc)
for scanner.Scan() {
lines++
s := scanner.Text()
prefix := linePrefix(s)
if prefix >= 0 {
counts[prefix] = counts[prefix] + 1
if prefix > max {
max = prefix
}
}
}
min := max
for p, _ := range counts {
if p < min {
min = p
}
}
return min, nil
}

View File

@ -81,6 +81,7 @@ type SimpleLineSource struct {
lines []string
size int
curr int
prefix int
}
func (sls *SimpleLineSource) Next() (line Line, done bool, err error) {
@ -88,16 +89,22 @@ func (sls *SimpleLineSource) Next() (line Line, done bool, err error) {
return Line{}, true, nil
}
sls.curr++
return NewLine(sls.lines[sls.curr-1], sls.curr, &sls.context), false, nil
l := NewLine(sls.lines[sls.curr-1], sls.curr, &sls.context)
for i := 0; i < sls.prefix; i++ {
l.Parse.Next()
l.Parse.Ignore()
}
return l, false, nil
}
func (sls SimpleLineSource) Context() Context {
return sls.context
}
func NewSimpleLineSource(context Context, ls []string) LineSource {
func NewSimpleLineSource(context Context, ls []string, prefix int) LineSource {
return &SimpleLineSource{
context: context,
lines: ls,
size: len(ls),
prefix: prefix,
}
}

View File

@ -23,5 +23,5 @@ func (m M) LineSource(flavor flavors.F, in inst.I, macroCall int) (lines.LineSou
}
ls = append(ls, subbed)
}
return lines.NewSimpleLineSource(context, ls), nil
return lines.NewSimpleLineSource(context, ls, 0), nil
}