1
0
mirror of https://github.com/zellyn/go6502.git synced 2025-04-14 17:41:01 +00:00

Can now assemble autostart ROM

This commit is contained in:
Zellyn Hunter 2014-05-31 12:55:36 -07:00
parent 9a7f1c2cca
commit 0a28127420
8 changed files with 322 additions and 88 deletions

View File

@ -33,6 +33,7 @@ func init() {
var infile = flag.String("in", "", "input file")
var outfile = flag.String("out", "", "output file")
var listfile = flag.String("listing", "", "listing 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")
@ -118,4 +119,26 @@ func main() {
fmt.Fprintf(os.Stderr, "format must be binary or ihex; got '%s'\n", *format)
os.Exit(1)
}
if *listfile != "" {
list, err := os.Create(*listfile)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer func() {
err := list.Close()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}()
err = a.GenerateListing(list, 3)
if err != nil {
fmt.Fprintf(os.Stderr, "Error while generating %s: %s", *listfile, err)
os.Exit(1)
}
}
}

View File

@ -2,6 +2,7 @@ package asm
import (
"fmt"
"io"
"path/filepath"
"github.com/zellyn/go6502/asm/flavors"
@ -266,3 +267,45 @@ func (a *Assembler) Membuf() (*membuf.Membuf, error) {
}
return m, nil
}
func (a *Assembler) GenerateListing(w io.Writer, width int) error {
for _, in := range a.Insts {
if !in.Final {
return in.Errorf("cannot finalize value: %s", in)
}
if !in.AddrKnown {
return in.Errorf("address unknown: %s", in)
}
for i := 0; i < len(in.Data) || i < width; i++ {
if i%width == 0 {
s := fmt.Sprintf("%04x:", int(in.Addr)+i)
if i > 0 {
s = "\n" + s
}
if _, err := fmt.Fprint(w, s); err != nil {
return err
}
}
s := " "
if i < len(in.Data) {
s = fmt.Sprintf(" %02x", in.Data[i])
}
if _, err := fmt.Fprint(w, s); err != nil {
return err
}
if i == width-1 {
if _, err := fmt.Fprint(w, " "+in.Line.Text()); err != nil {
return err
}
}
}
if _, err := fmt.Fprint(w, "\n"); err != nil {
return err
}
}
return nil
}

View File

@ -20,6 +20,7 @@ type SimpleContext struct {
addr int32
lastLabel string
clearMesg string // Saved message describing why Addr was cleared.
highbit byte // OR-mask for ASCII high bit
}
type symbolValue struct {
@ -91,4 +92,5 @@ func (sc *SimpleContext) RemoveChanged() {
func (sc *SimpleContext) Clear() {
sc.symbols = make(map[string]symbolValue)
sc.highbit = 0x00
}

View File

@ -24,12 +24,10 @@ const macroNameChars = Letters + Digits + "._"
const fileChars = Letters + Digits + "."
const operatorChars = "+-*/<>="
// 40 spaces = comment column
const comment_whitespace_prefix = " "
type DirectiveInfo struct {
Type inst.Type
Func func(inst.I, *lines.Parse) (inst.I, error)
Var int
}
type Requiredness int
@ -47,9 +45,13 @@ type Base struct {
Operators map[string]expr.Operator
context.SimpleContext
context.LabelerBase
LabelChars string
LabelColons Requiredness
ExplicitARegister Requiredness
LabelChars string
LabelColons Requiredness
ExplicitARegister Requiredness
ExtraCommenty func(string) bool
TwoSpacesIsComment bool // two spaces after command means comment field?
StringEndOptional bool // can omit closing delimeter from string args?
SetAsciiVariation func(*inst.I, *lines.Parse)
}
// Parse an entire instruction, or return an appropriate error.
@ -73,8 +75,8 @@ func (a *Base) ParseInstr(line lines.Line) (inst.I, error) {
in.DeclaredLine = uint16(i)
}
// Comment by virtue of long whitespace prefix
if strings.HasPrefix(lp.Rest(), comment_whitespace_prefix) {
// Flavor considers this line extra commenty for some reason?
if a.ExtraCommenty != nil && a.ExtraCommenty(lp.Rest()) {
in.Type = inst.TypeNone
return in, nil
}
@ -89,6 +91,16 @@ func (a *Base) ParseInstr(line lines.Line) (inst.I, error) {
// See if we have a label at the start
if lp.AcceptRun(a.LabelChars) {
in.Label = lp.Emit()
// Some need colons after labels, some allow them.
switch a.LabelColons {
case ReqRequired:
if !lp.Consume(":") {
return inst.I{}, line.Errorf("label '%s' must end in colon", in.Label)
}
case ReqOptional:
lp.Consume(":")
}
}
// Ignore whitespace at the start or after the label.
@ -122,6 +134,7 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
in.Command = lp.Emit()
if dir, ok := a.Directives[in.Command]; ok {
in.Type = dir.Type
in.Var = dir.Var
if dir.Func == nil {
return in, nil
}
@ -217,7 +230,11 @@ func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
// Nothing else on the line? Must be MODE_A
lp.Consume(whitespace)
if lp.Consume(whitespace) || lp.Peek() == lines.Eol {
if !a.TwoSpacesIsComment {
lp.IgnoreRun(whitespace)
}
if (a.TwoSpacesIsComment && lp.Consume(whitespace)) || lp.Peek() == lines.Eol || lp.Peek() == ';' {
// Nothing left on line except comments.
if !summary.AnyModes(opcodes.MODE_A) {
return i, in.Errorf("%s with no arguments", in.Command)
}
@ -243,6 +260,29 @@ func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
if err != nil {
return i, err
}
if !indirect && (expr.Text == "a" || expr.Text == "A") {
if !summary.AnyModes(opcodes.MODE_A) {
return i, in.Errorf("%s doesn't support A mode", in.Command)
}
switch a.ExplicitARegister {
case ReqDisallowed:
return i, in.Errorf("Assembler flavor doesn't support A mode", in.Command)
case ReqOptional, ReqRequired:
op, ok := summary.OpForMode(opcodes.MODE_A)
if !ok {
panic(fmt.Sprintf("%s doesn't support accumulator mode", in.Command))
}
in.Data = []byte{op.Byte}
in.WidthKnown = true
in.MinWidth = 1
in.MaxWidth = 1
in.Final = true
in.Mode = opcodes.MODE_A
in.Exprs = nil
return in, nil
}
}
in.Exprs = append(in.Exprs, expr)
comma := lp.Consume(",")
if comma {
@ -293,12 +333,19 @@ func (a *Base) ParseAddress(in inst.I, lp *lines.Parse) (inst.I, error) {
func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(whitespace)
a.SetAsciiVariation(&in, lp)
var invert, invertLast byte
if lp.Consume("-") {
switch in.Var {
case inst.DataAscii:
case inst.DataAsciiHi:
invert = 0x80
}
if in.Command == ".AT" {
case inst.DataAsciiFlip:
invertLast = 0x80
case inst.DataAsciiHiFlip:
invert = 0x80
invertLast = 0x80
default:
panic(fmt.Sprintf("ParseAscii with weird Variation: %d", in.Var))
}
delim := lp.Next()
if delim == lines.Eol || strings.IndexRune(whitespace, delim) >= 0 {
@ -307,7 +354,7 @@ func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.Ignore()
lp.AcceptUntil(string(delim))
delim2 := lp.Next()
if delim != delim2 {
if delim != delim2 && !(delim2 == lines.Eol && a.StringEndOptional) {
return inst.I{}, in.Errorf("%s: expected closing delimeter '%s'; got '%s'", in.Command, delim, delim2)
}
lp.Backup()

View File

@ -1,14 +1,18 @@
package redbook
import (
"fmt"
"github.com/zellyn/go6502/asm/expr"
"github.com/zellyn/go6502/asm/flavors/oldschool"
"github.com/zellyn/go6502/asm/inst"
"github.com/zellyn/go6502/asm/lines"
)
// RedBook implements a Redbook-listing-compatible-ish assembler flavor.
type RedBook struct {
oldschool.Base
asciiHi bool
}
func New() *RedBook {
@ -17,35 +21,35 @@ func New() *RedBook {
a.LabelChars = oldschool.Letters + oldschool.Digits + "."
a.LabelColons = oldschool.ReqOptional
a.ExplicitARegister = oldschool.ReqRequired
a.StringEndOptional = true
a.Directives = map[string]oldschool.DirectiveInfo{
".IN": {inst.TypeInclude, a.ParseInclude},
"ORG": {inst.TypeOrg, a.ParseAddress},
"OBJ": {inst.TypeNone, nil},
".TF": {inst.TypeNone, nil},
".EN": {inst.TypeEnd, a.ParseNoArgDir},
"EQU": {inst.TypeEqu, a.ParseEquate},
".DA": {inst.TypeData, a.ParseData},
"DFB": {inst.TypeDataBytes, a.ParseData},
"DW": {inst.TypeDataWords, a.ParseData},
".HS": {inst.TypeData, a.ParseHexString},
".AS": {inst.TypeData, a.ParseAscii},
".AT": {inst.TypeData, a.ParseAscii},
".BS": {inst.TypeBlock, a.ParseBlockStorage},
".LIST": {inst.TypeNone, nil},
".PG": {inst.TypeNone, nil},
".DO": {inst.TypeIfdef, a.ParseDo},
".ELSE": {inst.TypeIfdefElse, a.ParseNoArgDir},
".FIN": {inst.TypeIfdefEnd, a.ParseNoArgDir},
".MA": {inst.TypeMacroStart, a.ParseMacroStart},
".EM": {inst.TypeMacroEnd, a.ParseNoArgDir},
".US": {inst.TypeNone, a.ParseNotImplemented},
"PAGE": {inst.TypeNone, nil}, // New page
"LST": {inst.TypeNone, nil}, // Listing on/off
"SBTL": {inst.TypeNone, nil}, // Subtitle
"SKP": {inst.TypeNone, nil}, // Skip lines
"REP": {inst.TypeNone, nil}, // Repeat character
"CHR": {inst.TypeNone, nil}, // Set repeated character
".IN": {inst.TypeInclude, a.ParseInclude, 0},
"ORG": {inst.TypeOrg, a.ParseAddress, 0},
"OBJ": {inst.TypeNone, nil, 0},
".TF": {inst.TypeNone, nil, 0},
".EN": {inst.TypeEnd, a.ParseNoArgDir, 0},
"EQU": {inst.TypeEqu, a.ParseEquate, 0},
"DFB": {inst.TypeData, a.ParseData, inst.DataBytes},
"DW": {inst.TypeData, a.ParseData, inst.DataWordsLe},
"DDB": {inst.TypeData, a.ParseData, inst.DataWordsBe},
"ASC": {inst.TypeData, a.ParseAscii, inst.DataAscii},
"DCI": {inst.TypeData, a.ParseAscii, inst.DataAsciiFlip},
".BS": {inst.TypeBlock, a.ParseBlockStorage, 0},
".LIST": {inst.TypeNone, nil, 0},
".PG": {inst.TypeNone, nil, 0},
".DO": {inst.TypeIfdef, a.ParseDo, 0},
".ELSE": {inst.TypeIfdefElse, a.ParseNoArgDir, 0},
".FIN": {inst.TypeIfdefEnd, a.ParseNoArgDir, 0},
".MA": {inst.TypeMacroStart, a.ParseMacroStart, 0},
".EM": {inst.TypeMacroEnd, a.ParseNoArgDir, 0},
".US": {inst.TypeNone, a.ParseNotImplemented, 0},
"PAGE": {inst.TypeNone, nil, 0}, // New page
"LST": {inst.TypeNone, nil, 0}, // Listing on/off
"SBTL": {inst.TypeNone, nil, 0}, // Subtitle
"SKP": {inst.TypeNone, nil, 0}, // Skip lines
"REP": {inst.TypeNone, nil, 0}, // Repeat character
"CHR": {inst.TypeNone, nil, 0}, // Set repeated character
}
a.Operators = map[string]expr.Operator{
"*": expr.OpMul,
@ -57,5 +61,21 @@ func New() *RedBook {
"=": expr.OpEq,
}
a.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) {
if in.Command == "ASC" {
if a.asciiHi {
in.Var = inst.DataAsciiHi
} else {
in.Var = inst.DataAscii
}
return
}
if in.Command == "DCI" {
in.Var = inst.DataAsciiFlip
} else {
panic(fmt.Sprintf("Unknown ascii directive: '%s'", in.Command))
}
}
return a
}

View File

@ -1,11 +1,17 @@
package scma
import (
"strings"
"github.com/zellyn/go6502/asm/expr"
"github.com/zellyn/go6502/asm/flavors/oldschool"
"github.com/zellyn/go6502/asm/inst"
"github.com/zellyn/go6502/asm/lines"
)
// 40 spaces = comment column
const commentWhitespacePrefix = " "
// SCMA implements the S-C Macro Assembler-compatible assembler flavor.
// See http://www.txbobsc.com/scsc/ and http://stjarnhimlen.se/apple2/
type SCMA struct {
@ -17,28 +23,29 @@ func New() *SCMA {
a.LabelChars = oldschool.Letters + oldschool.Digits + ".:"
a.LabelColons = oldschool.ReqDisallowed
a.ExplicitARegister = oldschool.ReqDisallowed
a.TwoSpacesIsComment = true
a.Directives = map[string]oldschool.DirectiveInfo{
".IN": {inst.TypeInclude, a.ParseInclude},
".OR": {inst.TypeOrg, a.ParseAddress},
".TA": {inst.TypeTarget, a.ParseNotImplemented},
".TF": {inst.TypeNone, nil},
".EN": {inst.TypeEnd, a.ParseNoArgDir},
".EQ": {inst.TypeEqu, a.ParseEquate},
".DA": {inst.TypeData, a.ParseData},
".HS": {inst.TypeData, a.ParseHexString},
".AS": {inst.TypeData, a.ParseAscii},
".AT": {inst.TypeData, a.ParseAscii},
".BS": {inst.TypeBlock, a.ParseBlockStorage},
".TI": {inst.TypeNone, nil},
".LIST": {inst.TypeNone, nil},
".PG": {inst.TypeNone, nil},
".DO": {inst.TypeIfdef, a.ParseDo},
".ELSE": {inst.TypeIfdefElse, a.ParseNoArgDir},
".FIN": {inst.TypeIfdefEnd, a.ParseNoArgDir},
".MA": {inst.TypeMacroStart, a.ParseMacroStart},
".EM": {inst.TypeMacroEnd, a.ParseNoArgDir},
".US": {inst.TypeNone, a.ParseNotImplemented},
".IN": {inst.TypeInclude, a.ParseInclude, 0},
".OR": {inst.TypeOrg, a.ParseAddress, 0},
".TA": {inst.TypeTarget, a.ParseNotImplemented, 0},
".TF": {inst.TypeNone, nil, 0},
".EN": {inst.TypeEnd, a.ParseNoArgDir, 0},
".EQ": {inst.TypeEqu, a.ParseEquate, 0},
".DA": {inst.TypeData, a.ParseData, inst.DataMixed},
".HS": {inst.TypeData, a.ParseHexString, inst.DataBytes},
".AS": {inst.TypeData, a.ParseAscii, inst.DataBytes},
".AT": {inst.TypeData, a.ParseAscii, inst.DataBytes},
".BS": {inst.TypeBlock, a.ParseBlockStorage, 0},
".TI": {inst.TypeNone, nil, 0},
".LIST": {inst.TypeNone, nil, 0},
".PG": {inst.TypeNone, nil, 0},
".DO": {inst.TypeIfdef, a.ParseDo, 0},
".ELSE": {inst.TypeIfdefElse, a.ParseNoArgDir, 0},
".FIN": {inst.TypeIfdefEnd, a.ParseNoArgDir, 0},
".MA": {inst.TypeMacroStart, a.ParseMacroStart, 0},
".EM": {inst.TypeMacroEnd, a.ParseNoArgDir, 0},
".US": {inst.TypeNone, a.ParseNotImplemented, 0},
}
a.Operators = map[string]expr.Operator{
"*": expr.OpMul,
@ -50,6 +57,27 @@ func New() *SCMA {
"=": expr.OpEq,
}
a.ExtraCommenty = func(s string) bool {
//Comment by virtue of long whitespace prefix
return strings.HasPrefix(s, commentWhitespacePrefix)
}
a.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) {
// For S-C Assembler, leading "-" flips high bit
invert := lp.Consume("-")
invertLast := in.Command == ".AT"
switch {
case !invert && !invertLast:
in.Var = inst.DataAscii
case !invert && invertLast:
in.Var = inst.DataAsciiFlip
case invert && !invertLast:
in.Var = inst.DataAsciiHi
case invert && invertLast:
in.Var = inst.DataAsciiHiFlip
}
}
return a
}

View File

@ -51,14 +51,20 @@ func TestSimpleCommonFunctions(t *testing.T) {
{mm, " ORG $D000", "{org $d000}", ""},
// {ss, " .TA *-1234", "{target (- * $04d2)}", ""},
{ss, " .DA $1234", "{data $1234}", "3412"},
{aa, " dw $1234", "{data $1234}", "3412"},
{mm, " DW $1234", "{data $1234}", "3412"},
{aa, " dw $1234", "{data/wle $1234}", "3412"},
{mm, " DW $1234", "{data/wle $1234}", "3412"},
{ss, " .DA/$1234,#$1234,$1234", "{data (msb $1234),(lsb $1234),$1234}", "12343412"},
{rb, " DFB $12", "{data/b $0012}", "12"},
{rb, " DFB $12,$34,$1234", "{data/b $0012,$0034,$1234}", "123434"},
{rb, " DW $12,$34,$1234", "{data/wle $0012,$0034,$1234}", "120034003412"},
{rb, " DDB $12,$34,$1234", "{data/wbe $0012,$0034,$1234}", "001200341234"},
{ss, " ROL", "{ROL/a}", "2a"},
{aa, " rol a", "{ROL/a}", "2a"},
{rb, " ROL A", "{ROL/a}", "2a"},
{rb, " ROL A", "{ROL/a}", "2a"}, // two spaces is no big deal
{mm, " ROL", "{ROL/a}", "2a"},
{ss, " ROL Comment after two spaces", "{ROL/a}", "2a"},
{ss, " ROL X", "{ROL/a}", "2a"}, // two spaces = comment
{ss, " ROL $1234", "{ROL/abs $1234}", "2e3412"},
{aa, " rol $1234", "{ROL/abs $1234}", "2e3412"},
{mm, " ROL $1234", "{ROL/abs $1234}", "2e3412"},
@ -107,17 +113,22 @@ func TestSimpleCommonFunctions(t *testing.T) {
{ss, " LDA ($12,X)", "{LDA/indX $0012}", "a112"},
{aa, " lda ($12,x)", "{LDA/indX $0012}", "a112"},
{mm, " LDA ($12,X)", "{LDA/indX $0012}", "a112"},
{ss, ` .AS "ABC"`, "{data}", "414243"},
{ss, ` .AT "ABC"`, "{data}", "4142c3"},
{ss, ` .AS /ABC/`, "{data}", "414243"},
{ss, ` .AT /ABC/`, "{data}", "4142c3"},
{ss, ` .AS -"ABC"`, "{data}", "c1c2c3"},
{ss, ` .AT -"ABC"`, "{data}", "c1c243"},
{ss, ` .AS -dABCd`, "{data}", "c1c2c3"},
{ss, ` .AT -dABCd`, "{data}", "c1c243"},
{ss, " .HS 0001ffAb", "{data}", "0001ffab"},
{ss, ` .AS "ABC"`, "{data/b}", "414243"},
{ss, ` .AT "ABC"`, "{data/b}", "4142c3"},
{ss, ` .AS /ABC/`, "{data/b}", "414243"},
{ss, ` .AT /ABC/`, "{data/b}", "4142c3"},
{ss, ` .AS -"ABC"`, "{data/b}", "c1c2c3"},
{ss, ` .AT -"ABC"`, "{data/b}", "c1c243"},
{ss, ` .AS -dABCd`, "{data/b}", "c1c2c3"},
{ss, ` .AT -dABCd`, "{data/b}", "c1c243"},
{rb, ` ASC "ABC"`, "{data/b}", "414243"},
{rb, ` ASC $ABC$ ;comment`, "{data/b}", "414243"},
{rb, ` ASC $ABC`, "{data/b}", "414243"},
{rb, ` DCI "ABC"`, "{data/b}", "4142c3"},
{rb, ` ASC -ABC-`, "{data/b}", "414243"},
{ss, " .HS 0001ffAb", "{data/b}", "0001ffab"},
{ss, "A.B .EQ *-C.D", "{= 'A.B' (- * C.D)}", ""},
{ss, " .BS $8", "{block $0008}", ""},
{ss, " .BS $8", "{block $0008}", "xxxxxxxxxxxxxxxx"},
{ss, " .DO A<$3", "{if (< A $0003)}", ""},
{ss, " .ELSE", "{else}", ""},
{ss, " .FIN", "{endif}", ""},
@ -168,8 +179,25 @@ func TestSimpleCommonFunctions(t *testing.T) {
if tt.b != "?" {
hx := hex.EncodeToString(inst.Data)
if hx != tt.b {
t.Fatalf(`%d. %T.ParseInstr("%s").Data = [%s]; want [%s]`, i, tt.a, tt.i, hx, tt.b)
// xxxxxx sets the width, but doesn't expect actual data
if hx != tt.b && (len(tt.b) == 0 || tt.b[0] != 'x') {
t.Errorf(`%d. %T.ParseInstr("%s").Data = [%s]; want [%s]`, i, tt.a, tt.i, hx, tt.b)
continue
}
// Check length
w := uint16(len(tt.b) / 2)
if !inst.WidthKnown {
t.Errorf(`%d. %s.WidthKnown is false`, i, inst)
continue
}
if inst.MinWidth != inst.MaxWidth {
t.Errorf(`%d. %s: MinWidth(%d) != MaxWidth(%d)`, i, inst, inst.MinWidth, inst.MaxWidth)
continue
}
if inst.MinWidth != w {
t.Errorf(`%d. %s.MinWidth=%d; want %d`, i, inst, inst.MinWidth, w)
continue
}
}
}

View File

@ -25,9 +25,7 @@ const (
TypeIfdefElse // Ifdef else block
TypeIfdefEnd // Ifdef end
TypeInclude // Include a file
TypeData // Data: hex, ascii, etc., etc.
TypeDataBytes // Data: expressions, but forced to one byte per
TypeDataWords // Data: expressions, but forced to one word per
TypeData // Data
TypeBlock // Block storage
TypeOrg // Where to store assembled code
TypeTarget // Target address to use for jumps, labels, etc.
@ -37,6 +35,18 @@ const (
TypeEnd // End assembly
)
// Variants for "TypeData" instructions.
const (
DataBytes = iota // Data: expressions, but forced to one byte per
DataMixed // Bytes or words (LE), depending on individual expression widths
DataWordsLe // Data: expressions, but forced to one word per, little-endian
DataWordsBe // Data: expressions, but forced to one word per, big-endian
DataAscii // Data: from ASCII strings, high bit clear
DataAsciiHi // Data: from ASCII strings, high bit set
DataAsciiFlip // Data: from ASCII strings, high bit clear, except last char
DataAsciiHiFlip // Data: from ASCII strings, high bit set, except last char
)
type I struct {
Type Type // Type of instruction
Label string // Text of label part
@ -54,10 +64,11 @@ type I struct {
ZeroMode opcodes.AddressingMode // Possible ZP-option mode
ZeroOp byte // Possible ZP-option Opcode
Value uint16 // For Equates, the value
DeclaredLine uint16
Line *lines.Line
Addr uint16
AddrKnown bool
DeclaredLine uint16 // Line number listed in file
Line *lines.Line // Line object for this line
Addr uint16 // Current memory address
AddrKnown bool // Whether the current memory address is known
Var int // Variant of instruction type
}
func (i I) TypeString() string {
@ -81,7 +92,20 @@ func (i I) TypeString() string {
case TypeInclude:
return "inc"
case TypeData:
return "data"
switch i.Var {
case DataMixed:
return "data"
case DataBytes:
return "data/b"
case DataWordsLe:
return "data/wle"
case DataWordsBe:
return "data/wbe"
case DataAscii, DataAsciiHi, DataAsciiFlip, DataAsciiHiFlip:
return "data/b"
default:
panic(fmt.Sprintf("unknown data variant: %d", i.Var))
}
case TypeBlock:
return "block"
case TypeOrg:
@ -243,7 +267,15 @@ func (i *I) computeData(c context.Context, setWidth bool, final bool) (bool, err
data := []byte{}
var width uint16
for _, e := range i.Exprs {
w := e.Width()
var w uint16
switch i.Var {
case DataMixed:
w = e.Width()
case DataBytes:
w = 1
case DataWordsLe, DataWordsBe:
w = 2
}
width += w
val, labelMissing, err := e.CheckedEval(c, i.Line)
if err != nil && !labelMissing {
@ -255,11 +287,22 @@ func (i *I) computeData(c context.Context, setWidth bool, final bool) (bool, err
return false, err
}
}
switch w {
case 1:
switch i.Var {
case DataMixed:
switch w {
case 1:
data = append(data, byte(val))
case 2:
data = append(data, byte(val), byte(val>>8))
}
case DataBytes:
data = append(data, byte(val))
case 2:
case DataWordsLe:
data = append(data, byte(val), byte(val>>8))
case DataWordsBe:
data = append(data, byte(val>>8), byte(val))
default:
panic(fmt.Sprintf("Unknown data variant handed to computeData: %d", i.Var))
}
}
i.MinWidth = width
@ -390,10 +433,10 @@ func (i *I) computeOp(c context.Context, setWidth bool, final bool) (bool, error
// Found both current and target addresses
offset := int32(val) - (int32(curr) + 2)
if offset > 127 {
return false, i.Errorf("%s cannot jump forward %d (max 127)", i.Command, offset)
return false, i.Errorf("%s cannot jump forward %d (max 127) from $%04x to $%04x", i.Command, offset, curr+2, val)
}
if offset < -128 {
return false, i.Errorf("%s cannot jump back %d (max -128)", i.Command, offset)
return false, i.Errorf("%s cannot jump back %d (max -128) from $%04x to $%04x", i.Command, offset, curr+2, val)
}
val = uint16(offset)
}