[script] Better auto label support

Labels are now their own object instead of just a string.  This allows
for a bit more control with them.  Labels can also now have comments.

Additionally, add the ability to load user-defined labels from a file.
The format of this file is subject to change, but for now it is just a
simple text file.  Each line of the file is a label definition.  Each
line has three fields, separated by spaces.  The first field is the
address, second is the label name, and the rest of the line is a
comment.  If a label name is just a dollar sign ($) it will not have a
name.  This is used when adding comments without a bespoke label.
This commit is contained in:
Zorchenhimer 2025-09-06 22:38:39 -04:00
parent 29e48f3cac
commit 4b9f04874b
Signed by: Zorchenhimer
GPG Key ID: 70A1AB767AAB9C20
4 changed files with 126 additions and 8 deletions

View File

@ -5,8 +5,11 @@ import (
"os"
"strings"
"strconv"
"bufio"
"slices"
"github.com/alexflint/go-arg"
"git.zorchenhimer.com/Zorchenhimer/go-studybox/script"
)
@ -14,6 +17,8 @@ type Arguments struct {
Input string `arg:"positional,required"`
Output string `arg:"positional"`
StartAddr string `arg:"--start" default:"0x6000" help:"base address for the start of the script"`
LabelFile string `arg:"--labels" help:"file containing address/label pairs"`
start int
}
@ -38,6 +43,18 @@ func run(args *Arguments) error {
return err
}
if args.LabelFile != "" {
labels, err := parseLabelFile(args.LabelFile)
if err != nil {
return err
}
for _, label := range labels {
//fmt.Printf("%#v\n", label)
scr.Labels[label.Address] = label
}
}
outfile := os.Stdout
if args.Output != "" {
outfile, err = os.Create(args.Output)
@ -64,6 +81,62 @@ func run(args *Arguments) error {
return nil
}
func parseLabelFile(filename string) ([]*script.Label, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
labels := []*script.Label{}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
line = strings.ReplaceAll(line, "\t", " ")
parts := strings.Split(line, " ")
parts = slices.DeleteFunc(parts, func(str string) bool {
return str == ""
})
if len(parts) < 2 {
fmt.Println("Ignoring", line)
continue
}
if strings.HasPrefix(parts[0], "$") {
parts[0] = "0x"+parts[0][1:]
}
addr, err := strconv.ParseInt(parts[0], 0, 32)
if err != nil {
fmt.Printf("Address parse error for %q: %s\n", line, err)
continue
}
lbl := &script.Label{
Name: parts[1],
Address: int(addr),
}
if lbl.Name == "$" {
lbl.Name = ""
}
if len(parts) > 2 {
lbl.Comment = strings.Join(parts[2:], " ")
}
labels = append(labels, lbl)
}
return labels, nil
}
func main() {
args := &Arguments{}
arg.MustParse(args)

View File

@ -5,6 +5,42 @@ import (
"os"
)
type Label struct {
Address int
Name string
Comment string
FarLabel bool
}
func AutoLabel(address int) *Label {
return &Label{
Address: address,
Name: fmt.Sprintf("L%04X", address),
}
}
func AutoLabelVar(address int) *Label {
return &Label{
Address: address,
Name: fmt.Sprintf("Var_%04X", address),
}
}
func AutoLabelFar(address int) *Label {
return &Label{
Address: address,
Name: fmt.Sprintf("F%04X", address),
FarLabel: true,
}
}
func NewLabel(address int, name string) *Label {
return &Label{
Address: address,
Name: name,
}
}
func ParseFile(filename string, startAddr int) (*Script, error) {
rawfile, err := os.ReadFile(filename)
if err != nil {
@ -24,7 +60,7 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
Warnings: []string{},
StackAddress: (int(rawinput[1])<<8) | int(rawinput[0]),
StartAddress: startAddr,
Labels: make(map[int]string), // map[location]name
Labels: make(map[int]*Label), // map[location]name
}
tokenMap := make(map[int]*Token)
@ -112,7 +148,7 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
if tok.Offset == addr {
tok.IsTarget = true
found = true
script.Labels[addr] = fmt.Sprintf("L%04X", addr)
script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
break
}
}
@ -133,7 +169,7 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
if tok.Offset == addr {
tok.IsTarget = true
found = true
script.Labels[addr] = fmt.Sprintf("L%04X", addr)
script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
break
}
}
@ -152,7 +188,7 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
addr := t.Inline[0].Int()
if tok, ok := tokenMap[addr]; ok {
tok.IsVariable = true
script.Labels[addr] = fmt.Sprintf("Var_%04X", addr)
script.Labels[addr] = AutoLabelVar(addr) //fmt.Sprintf("Var_%04X", addr)
}
}
}

View File

@ -10,5 +10,6 @@ type Script struct {
StartAddress int
StackAddress int
Labels map[int]string
//Labels map[int]string
Labels map[int]*Label
}

View File

@ -15,7 +15,7 @@ type Token struct {
Instruction *Instruction
}
func (t Token) String(labels map[int]string) string {
func (t Token) String(labels map[int]*Label) string {
suffix := ""
switch t.Raw {
case 0x86:
@ -25,10 +25,18 @@ func (t Token) String(labels map[int]string) string {
prefix := ""
if t.IsTarget || t.IsVariable {
if lbl, ok := labels[t.Offset]; ok {
prefix = "\n"+lbl+":\n"
comment := ""
if lbl.Comment != "" {
comment = "; "+lbl.Comment+"\n"
}
prefix = "\n"+comment+lbl.Name+":\n"
} else {
prefix = fmt.Sprintf("\nL%04X:\n", t.Offset)
}
} else {
if lbl, ok := labels[t.Offset]; ok && lbl.Comment != "" {
suffix = " ; "+lbl.Comment+suffix
}
}
if t.Raw < 0x80 {
@ -56,7 +64,7 @@ func (t Token) String(labels map[int]string) string {
argstr := []string{}
for _, a := range t.Inline {
if lbl, ok := labels[a.Int()]; ok {
argstr = append(argstr, lbl)
argstr = append(argstr, lbl.Name)
} else {
argstr = append(argstr, a.HexString())
}