From 9992fd049a33c649fb167160a600ffc8faf5e779 Mon Sep 17 00:00:00 2001 From: Zellyn Hunter Date: Tue, 20 May 2014 08:23:20 -0700 Subject: [PATCH] working on assembler binary --- asm/asm.go | 55 ++++++++++++++-- asm/ihex/ihex.go | 107 +++++++++++++++++++++++++++++++ asm/ihex/ihex_test.go | 24 +++++++ asm/inst/instruction.go | 2 + asm/membuf/membuf.go | 82 ++++++++++++++++++++++++ asm/membuf/membuf_test.go | 130 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 394 insertions(+), 6 deletions(-) create mode 100644 asm/ihex/ihex.go create mode 100644 asm/ihex/ihex_test.go create mode 100644 asm/membuf/membuf.go create mode 100644 asm/membuf/membuf_test.go diff --git a/asm/asm.go b/asm/asm.go index 430826b..3db5024 100644 --- a/asm/asm.go +++ b/asm/asm.go @@ -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 +} diff --git a/asm/ihex/ihex.go b/asm/ihex/ihex.go new file mode 100644 index 0000000..afa8199 --- /dev/null +++ b/asm/ihex/ihex.go @@ -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 +} diff --git a/asm/ihex/ihex_test.go b/asm/ihex/ihex_test.go new file mode 100644 index 0000000..1f4dc5c --- /dev/null +++ b/asm/ihex/ihex_test.go @@ -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) + } +} diff --git a/asm/inst/instruction.go b/asm/inst/instruction.go index c216bfe..dbe8c34 100644 --- a/asm/inst/instruction.go +++ b/asm/inst/instruction.go @@ -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 { diff --git a/asm/membuf/membuf.go b/asm/membuf/membuf.go new file mode 100644 index 0000000..fd05470 --- /dev/null +++ b/asm/membuf/membuf.go @@ -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 +} diff --git a/asm/membuf/membuf_test.go b/asm/membuf/membuf_test.go new file mode 100644 index 0000000..fba94df --- /dev/null +++ b/asm/membuf/membuf_test.go @@ -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) + } +}