working on assembler binary

This commit is contained in:
Zellyn Hunter 2014-05-20 08:23:20 -07:00
parent 0f344d37fb
commit 9992fd049a
6 changed files with 394 additions and 6 deletions

View File

@ -2,11 +2,13 @@ package asm
import (
"fmt"
"path/filepath"
"github.com/zellyn/go6502/asm/flavors"
"github.com/zellyn/go6502/asm/inst"
"github.com/zellyn/go6502/asm/lines"
"github.com/zellyn/go6502/asm/macros"
"github.com/zellyn/go6502/asm/membuf"
)
type Assembler struct {
@ -99,8 +101,9 @@ func (a *Assembler) Load(filename string) error {
}
ifdefs = ifdefs[1:]
case inst.TypeInclude:
subContext := lines.Context{Filename: in.TextArg, Parent: in.Line}
subLs, err := lines.NewFileLineSource(in.TextArg, subContext, a.Opener)
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)
if err != nil {
return in.Errorf("error including file: %v", err)
}
@ -118,6 +121,26 @@ func (a *Assembler) Load(filename string) error {
return nil
}
func (a *Assembler) Assemble(filename string) error {
a.Reset()
if err := a.Load(filename); err != nil {
return err
}
// Setwidth pass if necessary.
if !a.Flavor.SetWidthsOnFirstPass() {
if _, err := a.Pass(true, false); err != nil {
return err
}
}
// Final pass.
if _, err := a.Pass(true, true); err != nil {
return err
}
return nil
}
func (a *Assembler) readMacro(in inst.I, ls lines.LineSource) error {
m := macros.M{
Name: in.TextArg,
@ -168,11 +191,15 @@ func (a *Assembler) passInst(in *inst.I, setWidth, final bool) (isFinal bool, er
panic(fmt.Sprintf("inst.I %s: WidthKnown=true, but MinWidth=%d, MaxWidth=%d", in, in.MinWidth, in.MaxWidth))
}
if in.WidthKnown && a.Flavor.AddrKnown() {
// Update address
if a.Flavor.AddrKnown() {
addr, _ := a.Flavor.GetAddr()
a.Flavor.SetAddr(addr + in.MinWidth)
} else {
if a.Flavor.AddrKnown() {
in.Addr = addr
in.AddrKnown = true
if in.WidthKnown {
a.Flavor.SetAddr(addr + in.MinWidth)
} else {
a.Flavor.ClearAddr(in.Sprintf("lost known address"))
}
}
@ -222,3 +249,19 @@ func (a *Assembler) Reset() {
a.Insts = nil
a.LastLabel = ""
}
func (a *Assembler) Membuf() (*membuf.Membuf, error) {
m := &membuf.Membuf{}
for _, in := range a.Insts {
if !in.Final {
return nil, in.Errorf("cannot finalize value: %s", in)
}
if !in.AddrKnown {
return nil, in.Errorf("address unknown: %s", in)
}
if in.MinWidth > 0 {
m.Write(int(in.Addr), in.Data)
}
}
return m, nil
}

107
asm/ihex/ihex.go Normal file
View File

@ -0,0 +1,107 @@
/* Package ihex implements enough of reading and writing Intel HEX files
to suffice for our purposes. */
package ihex
import (
"encoding/hex"
"fmt"
"io"
)
type RecWidth int
const (
Width16 RecWidth = 16
Width32 RecWidth = 32
)
type RecType byte
const (
TypeData RecType = 0 // Simple data
TypeEof RecType = 1 // End of file
TypeELAR RecType = 4 // Extended Linear Address Record
)
// A Chunk represents a run of bytes, starting at a specific address.
type Chunk struct {
Addr uint32
Data []byte
}
// A Writer writes bytes at addresses to an Intel HEX formatted file.
//
// As returned by NewWriter, a Writer writes records of 32 bytes,
// terminated by a newline. The exported fields can be changed to
// customize the details before the first call to Write.
type Writer struct {
Width RecWidth
UseCRLF bool // True to use \r\n as the line terminator
w io.Writer
extAddr uint32
}
func NewWriter(w io.Writer) *Writer {
return &Writer{
Width: Width32,
w: w,
}
}
func (w *Writer) eol() string {
if w.UseCRLF {
return "\r\n"
} else {
return "\n"
}
}
func (w *Writer) setExtendedAddress(addr uint32) error {
addr = addr &^ 0xffff
if w.extAddr == addr {
return nil
}
return w.writeRecord(TypeELAR, 0, []byte{byte(addr >> 24), byte(addr >> 16)})
}
func (w *Writer) Write(addr uint32, data []byte) error {
if err := w.setExtendedAddress(addr); err != nil {
return err
}
addr = addr & 0xffff
step := int(w.Width)
for i := 0; i < len(data); i += step {
end := i + step
if end > len(data) {
end = len(data)
}
if err := w.writeRecord(TypeData, addr+uint32(i), data[i:end]); err != nil {
return err
}
}
return nil
}
func (w *Writer) End() error {
_, err := fmt.Fprintf(w.w, ":00000001FF%s", w.eol())
return err
}
func checksum(data []byte) byte {
var s byte
for _, b := range data {
s += b
}
return (s ^ 0xff) + 1
}
func (w *Writer) writeRecord(t RecType, addr uint32, b []byte) error {
if len(b) > 0xff {
panic(fmt.Sprintf("writeRecord called with >255 bytes: %d", len(b)))
}
data := []byte{byte(len(b)), byte(addr >> 8), byte(addr), byte(t)}
data = append(data, b...)
data = append(data, checksum(data))
_, err := fmt.Fprintf(w.w, ":%s%s", hex.EncodeToString(data), w.eol())
return err
}

24
asm/ihex/ihex_test.go Normal file
View File

@ -0,0 +1,24 @@
package ihex
import (
"bytes"
"testing"
)
func TestWrite(t *testing.T) {
want := ":0501000061626364650b\n" +
":00000001FF\n"
var b bytes.Buffer
w := NewWriter(&b)
err := w.Write(0x100, []byte("abcde"))
if err != nil {
t.Fatal(err)
}
if err := w.End(); err != nil {
t.Fatal(err)
}
got := string(b.Bytes())
if got != want {
t.Errorf("Got \n%s; want \n%s", got, want)
}
}

View File

@ -54,6 +54,8 @@ type I struct {
Value uint16 // For Equates, the value
DeclaredLine uint16
Line *lines.Line
Addr uint16
AddrKnown bool
}
func (i I) TypeString() string {

82
asm/membuf/membuf.go Normal file
View File

@ -0,0 +1,82 @@
package membuf
import "fmt"
type Membuf struct {
// 0 means unused
// 1-257 means byte+1
data []int16
}
type Piece struct {
Addr int
Data []byte
}
func (m *Membuf) Delete(addr, size int) {
l := len(m.data)
for i := 0; i < size; i++ {
a := addr + i
if a >= l {
return
}
m.data[a] = 0
}
}
func (m *Membuf) Write(addr int, bs []byte) {
if addr < 0 {
panic(fmt.Sprintf("addr < 0: %d", addr))
}
if len(bs) == 0 {
return
}
e := addr + len(bs)
if len(m.data) < e {
m.data = append(m.data, make([]int16, e-len(m.data))...)
}
for i, b := range bs {
m.data[addr+i] = int16(b) + 1
}
}
func (m *Membuf) Pieces() []Piece {
ps := []Piece{}
var p *Piece
for a, d := range m.data {
if d == 0 {
if p != nil {
ps = append(ps, *p)
p = nil
}
} else {
if p == nil {
p = &Piece{Addr: a}
}
p.Data = append(p.Data, byte(d-1))
}
}
if p != nil {
ps = append(ps, *p)
}
return ps
}
func (m *Membuf) Piece(fill byte) Piece {
var p Piece
started := false
for a, d := range m.data {
if d == 0 {
if started {
p.Data = append(p.Data, fill)
}
} else {
if !started {
started = true
p.Addr = a
}
p.Data = append(p.Data, byte(d-1))
}
}
return p
}

130
asm/membuf/membuf_test.go Normal file
View File

@ -0,0 +1,130 @@
package membuf
import (
"reflect"
"testing"
)
func TestSimpleAdd(t *testing.T) {
m := Membuf{}
m.Write(0x100, []byte("abcde"))
got := m.Pieces()
want := []Piece{
{0x100, []byte("abcde")},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("m.Pieces()=%v; want %v", got, want)
}
}
func TestMultiAdd(t *testing.T) {
m := Membuf{}
m.Write(0x100, []byte("abcde"))
m.Write(0x108, []byte("fghij"))
got := m.Pieces()
want := []Piece{
{0x100, []byte("abcde")},
{0x108, []byte("fghij")},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("m.Pieces()=%v; want %v", got, want)
}
}
func TestOverlappingAdd(t *testing.T) {
m := Membuf{}
m.Write(0x100, []byte("abcde"))
m.Write(0x108, []byte("fghij"))
m.Write(0x104, []byte("klmno"))
got := m.Pieces()
want := []Piece{
{0x100, []byte("abcdklmnoghij")},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("m.Pieces()=%v; want %v", got, want)
}
}
func TestDelete(t *testing.T) {
m := Membuf{}
m.Write(0x100, []byte("abcdefghijklmnop"))
// Delete one off the end
m.Delete(0x10f, 1)
got := m.Pieces()
want := []Piece{
{0x100, []byte("abcdefghijklmno")},
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("m.Pieces()=%v; want %v", got, want)
}
// Delete a section overlapping the end
m.Delete(0x10e, 10)
got = m.Pieces()
want = []Piece{
{0x100, []byte("abcdefghijklmn")},
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("m.Pieces()=%v; want %v", got, want)
}
// Delete unfilled data: no change
m.Delete(0x1000, 10)
got = m.Pieces()
want = []Piece{
{0x100, []byte("abcdefghijklmn")},
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("m.Pieces()=%v; want %v", got, want)
}
// Delete one off the start
m.Delete(0x100, 1)
got = m.Pieces()
want = []Piece{
{0x101, []byte("bcdefghijklmn")},
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("m.Pieces()=%v; want %v", got, want)
}
// Delete a section overlapping the start
m.Delete(0xfe, 4)
got = m.Pieces()
want = []Piece{
{0x102, []byte("cdefghijklmn")},
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("m.Pieces()=%v; want %v", got, want)
}
// Delete a bit in the middle
m.Delete(0x106, 4)
got = m.Pieces()
want = []Piece{
{0x102, []byte("cdef")},
{0x10a, []byte("klmn")},
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("m.Pieces()=%v; want %v", got, want)
}
// Delete a zero-length chunk: nop
m.Delete(0x103, 0)
got = m.Pieces()
want = []Piece{
{0x102, []byte("cdef")},
{0x10a, []byte("klmn")},
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("m.Pieces()=%v; want %v", got, want)
}
// Delete everything
m.Delete(0x0, 0x1000)
got = m.Pieces()
if len(got) != 0 {
t.Fatalf("m.Pieces()=%v; want nil or {}", got, want)
}
}