1
0
mirror of https://github.com/zellyn/go6502.git synced 2024-06-06 20:29:34 +00:00

added a2as command-line assembler

This commit is contained in:
Zellyn Hunter 2014-05-21 21:44:23 -07:00
parent 9992fd049a
commit bcb02d5c41
4 changed files with 189 additions and 26 deletions

119
asm/a2as/a2as.go Normal file
View File

@ -0,0 +1,119 @@
package main
import (
"flag"
"fmt"
"os"
"strings"
"github.com/zellyn/go6502/asm"
"github.com/zellyn/go6502/asm/flavors"
"github.com/zellyn/go6502/asm/flavors/scma"
"github.com/zellyn/go6502/asm/ihex"
"github.com/zellyn/go6502/asm/lines"
)
var flavorsByName map[string]flavors.F
var flavor string
func init() {
flavorsByName = map[string]flavors.F{
"scma": scma.New(),
}
var names []string
for name := range flavorsByName {
names = append(names, name)
}
usage := fmt.Sprintf("assembler flavor: %s", strings.Join(names, ","))
flag.StringVar(&flavor, "flavor", "", usage)
}
var infile = flag.String("in", "", "input file")
var outfile = flag.String("out", "", "output 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")
func main() {
flag.Parse()
if *infile == "" {
fmt.Fprintln(os.Stderr, "no input file specified")
os.Exit(1)
}
if *outfile == "" {
fmt.Fprintln(os.Stderr, "no output file specified")
os.Exit(1)
}
if flavor == "" {
fmt.Fprintln(os.Stderr, "no flavor specified")
os.Exit(1)
}
f, ok := flavorsByName[flavor]
if !ok {
fmt.Fprintf(os.Stderr, "invalid flavor: '%s'\n", flavor)
os.Exit(1)
}
if *format != "binary" && *format != "ihex" {
fmt.Fprintf(os.Stderr, "format must be binary or ihex; got '%s'\n", *format)
os.Exit(1)
}
if *fill > 0xff {
fmt.Fprintf(os.Stderr, "fillbyte must be <= 255; got '%s'\n", *format)
os.Exit(1)
}
var o lines.OsOpener
a := asm.NewAssembler(f, o)
if err := a.Assemble(*infile); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
out, err := os.Create(*outfile)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer func() {
err := out.Close()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}()
m, err := a.Membuf()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
switch *format {
case "binary":
p := m.Piece(byte(*fill))
n, err := out.Write(p.Data)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if n != len(p.Data) {
fmt.Fprintf(os.Stderr, "Error writing to '%s': wrote %d of %d bytes", *outfile, n, len(p.Data))
os.Exit(1)
}
case "ihex":
w := ihex.NewWriter(out)
for _, p := range m.Pieces() {
if err := w.Write(p.Addr, p.Data); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
w.End()
default:
fmt.Fprintf(os.Stderr, "format must be binary or ihex; got '%s'\n", *format)
os.Exit(1)
}
}

View File

@ -232,8 +232,9 @@ func (a *Assembler) Pass(setWidth, final bool) (isFinal bool, err error) {
return isFinal, nil
}
// RawBytes returns the raw bytes, sequentially. Intended for testing,
// the return value gives no indication of address changes.
// RawBytes returns the raw bytes, sequentially in the order of the
// lines of the file. Intended for testing, the return value gives no
// indication of address changes.
func (a *Assembler) RawBytes() ([]byte, error) {
result := []byte{}
for _, in := range a.Insts {

View File

@ -2,14 +2,25 @@ package flavors
import (
"encoding/hex"
"reflect"
"strings"
"testing"
"github.com/zellyn/go6502/asm"
"github.com/zellyn/go6502/asm/flavors/scma"
"github.com/zellyn/go6502/asm/lines"
"github.com/zellyn/go6502/asm/membuf"
)
// h converts from hex or panics.
func h(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}
func TestMultiline(t *testing.T) {
o := lines.NewTestOpener()
@ -23,20 +34,21 @@ func TestMultiline(t *testing.T) {
i []string // main file: lines
ii map[string][]string // other files: lines
b string // bytes, expected
ps []membuf.Piece // output bytes expected
active bool
}{
// We cannot determine L2, so we go wide.
{ss, "Unknown label: wide", []string{
"L1 LDA L2-L1",
"L2 NOP",
}, nil, "ad0300ea", true},
}, nil, "ad0300ea", nil, true},
// sc-asm sets instruction widths on the first pass
{ss, "Later label: wide", []string{
" LDA FOO",
"FOO .EQ $FF",
" NOP",
}, nil, "adff00ea", true},
}, nil, "adff00ea", nil, true},
// Sub-labels
{ss, "Sublabels", []string{
@ -45,7 +57,7 @@ func TestMultiline(t *testing.T) {
"L2 BEQ .2",
".1 NOP",
".2 NOP",
}, nil, "f000eaf001eaea", true},
}, nil, "f000eaf001eaea", nil, true},
// Includes: one level deep
{ss, "Include A", []string{
@ -56,7 +68,7 @@ func TestMultiline(t *testing.T) {
"SUBFILE1": {
" LDA #$2a",
},
}, "f002a92aea", true},
}, "f002a92aea", nil, true},
// Ifdefs: simple at first
{ss, "Ifdef A", []string{
@ -67,7 +79,7 @@ func TestMultiline(t *testing.T) {
" LDA #$02",
" .FIN",
" NOP",
}, nil, "a901ea", true},
}, nil, "a901ea", nil, true},
// Ifdefs: else part
{ss, "Ifdef B", []string{
@ -78,7 +90,7 @@ func TestMultiline(t *testing.T) {
" LDA #$02",
" .FIN",
" NOP",
}, nil, "a902ea", true},
}, nil, "a902ea", nil, true},
// Ifdefs: multiple else, true
{ss, "Ifdef C", []string{
@ -93,7 +105,7 @@ func TestMultiline(t *testing.T) {
" LDA #$04",
" .FIN",
" NOP",
}, nil, "a901a903ea", true},
}, nil, "a901a903ea", nil, true},
// Ifdefs: multiple else, false
{ss, "Ifdef D", []string{
@ -108,7 +120,7 @@ func TestMultiline(t *testing.T) {
" LDA #$04",
" .FIN",
" NOP",
}, nil, "a902a904ea", true},
}, nil, "a902a904ea", nil, true},
// Ifdef based on org, true
{ss, "Ifdef/org A", []string{
@ -120,7 +132,7 @@ func TestMultiline(t *testing.T) {
" LDA #$03",
" .FIN",
" NOP",
}, nil, "a901a902ea", true},
}, nil, "a901a902ea", nil, true},
// Ifdef based on org, false
{ss, "Ifdef/org B", []string{
@ -132,7 +144,7 @@ func TestMultiline(t *testing.T) {
" LDA #$03",
" .FIN",
" NOP",
}, nil, "a901a903ea", true},
}, nil, "a901a903ea", nil, true},
// Macro, simple
{ss, "Macro, simple", []string{
@ -146,7 +158,7 @@ func TestMultiline(t *testing.T) {
"PTR .HS 0000",
"ZPTR .EQ $42",
" >INCD ZPTR",
}, nil, "ee0808d003ee09080000e642d002e643", true},
}, nil, "ee0808d003ee09080000e642d002e643", nil, true},
// Macro, conditional assembly
{ss, "Macro, conditional assembly", []string{
@ -171,7 +183,7 @@ func TestMultiline(t *testing.T) {
" >INCD $1234",
" >INCD $12,X",
" >INCD $1234,X",
}, nil, "e612d002e613ee3412d003ee3512f612d002f613fe3412d003fe3512", true},
}, nil, "e612d002e613ee3412d003ee3512f612d002f613fe3412d003fe3512", nil, true},
// Macros, nested
{ss, "Macros, nested", []string{
@ -189,13 +201,30 @@ func TestMultiline(t *testing.T) {
"SAM RTS",
"JOE RTS",
"TOM RTS",
}, nil, "200f08201108201008200f08201108606060", true},
}, nil, "200f08201108201008200f08201108606060", nil, true},
// Check outputs when origin is changed.
{ss, "Origin A", []string{
" .OR $1000",
" LDA #$01",
" .OR $2000",
" LDA #$02",
" .OR $1002",
" LDA #$03",
" NOP",
}, nil, "a901a902a903ea", []membuf.Piece{
{0x1000, h("a901a903ea")},
{0x2000, h("a902")},
}, true},
}
for i, tt := range tests {
if !tt.active {
continue
}
if tt.b == "" && len(tt.ps) == 0 {
t.Fatalf(`%d("%s"): test case must specify bytes or pieces`, i, tt.name)
}
tt.a.Reset()
o.Clear()
o["TESTFILE"] = strings.Join(tt.i, "\n")
@ -221,15 +250,29 @@ func TestMultiline(t *testing.T) {
t.Errorf(`%d("%s"): tt.a.Pass(true, true) couldn't finalize`, i, tt.name)
continue
}
bb, err := tt.a.RawBytes()
if err != nil {
t.Errorf(`%d("%s"): tt.a.RawBytes() failed: %s`, i, tt.name, err)
continue
if tt.b != "" {
bb, err := tt.a.RawBytes()
if err != nil {
t.Errorf(`%d("%s"): tt.a.RawBytes() failed: %s`, i, tt.name, err)
continue
}
hx := hex.EncodeToString(bb)
if hx != tt.b {
t.Errorf(`%d("%s"): tt.a.RawBytes()=[%s]; want [%s]`, i, tt.name, hx, tt.b)
continue
}
}
hx := hex.EncodeToString(bb)
if hx != tt.b {
t.Errorf(`%d("%s"): tt.a.RawBytes()=[%s]; want [%s]`, i, tt.name, hx, tt.b)
continue
if len(tt.ps) != 0 {
m, err := tt.a.Membuf()
if err != nil {
t.Errorf(`%d("%s"): tt.a.Membuf() failed: %s`, i, tt.name, err)
continue
}
ps := m.Pieces()
if !reflect.DeepEqual(ps, tt.ps) {
t.Errorf(`%d("%s"): tt.Membuf().Pieces() = %v; want %v`, i, tt.name, ps, tt.ps)
}
}
}
}

View File

@ -9,7 +9,7 @@ type Membuf struct {
}
type Piece struct {
Addr int
Addr uint32
Data []byte
}
@ -51,7 +51,7 @@ func (m *Membuf) Pieces() []Piece {
}
} else {
if p == nil {
p = &Piece{Addr: a}
p = &Piece{Addr: uint32(a)}
}
p.Data = append(p.Data, byte(d-1))
}
@ -73,7 +73,7 @@ func (m *Membuf) Piece(fill byte) Piece {
} else {
if !started {
started = true
p.Addr = a
p.Addr = uint32(a)
}
p.Data = append(p.Data, byte(d-1))
}