mirror of
https://github.com/zellyn/go6502.git
synced 2024-12-27 04:29:27 +00:00
added a2as command-line assembler
This commit is contained in:
parent
9992fd049a
commit
bcb02d5c41
119
asm/a2as/a2as.go
Normal file
119
asm/a2as/a2as.go
Normal 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)
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user