mirror of
https://github.com/zellyn/go6502.git
synced 2025-01-14 00:31:39 +00:00
working on assembler binary
This commit is contained in:
parent
0f344d37fb
commit
9992fd049a
55
asm/asm.go
55
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
|
||||
}
|
||||
|
107
asm/ihex/ihex.go
Normal file
107
asm/ihex/ihex.go
Normal 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
24
asm/ihex/ihex_test.go
Normal 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)
|
||||
}
|
||||
}
|
@ -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
82
asm/membuf/membuf.go
Normal 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
130
asm/membuf/membuf_test.go
Normal 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)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user